<?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=195.233.250.6</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=195.233.250.6"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/195.233.250.6"/>
	<updated>2026-04-10T14:54:45Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Lokale_Elektroniklieferanten&amp;diff=85490</id>
		<title>Lokale Elektroniklieferanten</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Lokale_Elektroniklieferanten&amp;diff=85490"/>
		<updated>2014-11-03T11:17:02Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: Bemerkungsfeld angepasst, da nurnoch der onlineshop ohne ladengeschäft existiert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Einleitung=&lt;br /&gt;
Diese Liste enthält &#039;&#039;&#039;Ladengeschäfte&#039;&#039;&#039;, bei denen man als Privatkunde lokal, vor Ort, elektronische Bauteile erhalten kann. Keine Flohmärkte, einmalige Veranstaltungen oder Geschäfte, die nur an gewerbliche Kunden verkaufen.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
Falls die Darstellungsart nicht gefällt oder Rubriken fehlen, so bitte nicht hier ändern, sondern das Template anpassen: [[Vorlage:ElektronikLieferant]]&amp;lt;br&amp;gt;&lt;br /&gt;
So soll das Template ausgefüllt werden:&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=hier Firmenname eintragen&lt;br /&gt;
|Straße=Straßenname, z.&amp;amp;nbsp;B. Musterstraße 123&lt;br /&gt;
|PLZ=PLZ, z.&amp;amp;nbsp;B. 12345&lt;br /&gt;
|Ort=Ort, z.&amp;amp;nbsp;B. München&lt;br /&gt;
|Telefon=Telnr., z.&amp;amp;nbsp;B. 012345/12341234&lt;br /&gt;
|Fax=Faxnr., z.&amp;amp;nbsp;B. 012345/12345234&lt;br /&gt;
|Öffnungszeiten=Öffnungszeiten eintragen&amp;lt;br&amp;gt;Neue Zeile mit &amp;quot;br&amp;quot; abgetrennt&lt;br /&gt;
|Weblink=http://www.mikrocontroller.net Link ohne umschliessende eckige Klammern&lt;br /&gt;
|Email=Emailadresse, z.&amp;amp;nbsp;B. xxx@yyy.de&lt;br /&gt;
|Bemerkung=ggf. Bemerkung, ansonsten Rubrik/Feld/Variable leer lassen&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=Deutschland=&lt;br /&gt;
==Baden-Württemberg==&lt;br /&gt;
===Aalen===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=mükra electronic shop GmbH&lt;br /&gt;
|Straße=Wilhelm-Zapf-Str. 9&lt;br /&gt;
|PLZ=73430&lt;br /&gt;
|Ort=Aalen&lt;br /&gt;
|Telefon=07361/610820&lt;br /&gt;
|Fax=07361/610821&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9.00 - 13.00 Uhr, 14.00 - 18.00 Uhr&amp;lt;br&amp;gt;Sa. 9.00 - 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.muekra.com/Filiale_Aalen&lt;br /&gt;
|Email=info@muekra.de&lt;br /&gt;
|Bemerkung=Keine SMD-Teile&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Esslingen===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=mükra electronic shop GmbH&lt;br /&gt;
|Straße=Bahnhofstr. 23&lt;br /&gt;
|PLZ=73728&lt;br /&gt;
|Ort=Esslingen&lt;br /&gt;
|Telefon=0711/355676&lt;br /&gt;
|Fax=0711/3108656&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9.00 - 13.00 Uhr, 14.30 - 18.00 Uhr&amp;lt;br&amp;gt;Sa. 9.00 - 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.muekra.com/Filiale_Esslingen&lt;br /&gt;
|Email=info@muekra.de&lt;br /&gt;
|Bemerkung=Keine Mikrocontroller, keine SMD-Teile (ausser einige wenige Transistoren)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Göppingen===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=mükra electronic shop GmbH&lt;br /&gt;
|Straße=Geislinger Str. 2&lt;br /&gt;
|PLZ=73033&lt;br /&gt;
|Ort=Göppingen&lt;br /&gt;
|Telefon=07161/9641718&lt;br /&gt;
|Fax=07161/9641730&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9.00 - 12.30 Uhr, 14.00 - 18.00 Uhr&amp;lt;br&amp;gt;Sa. 9.00 - 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.muekra.com/Filiale_Goeppingen&lt;br /&gt;
|Email=info@muekra.de&lt;br /&gt;
|Bemerkung=Keine Mikrocontroller, keine SMD-Teile (ausser einige wenige Transistoren). Es besteht die Möglichkeit die Bauteile die nicht im Sortiment vorhanden sind zu bestellen. Dies ist sehr günstig und passiert in 2-3 Werktagen.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Heilbronn===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=centralsystems GmbH&lt;br /&gt;
|Straße=Otto-Hahn-Straße 8&lt;br /&gt;
|PLZ=74078 &lt;br /&gt;
|Ort=Heilbronn&lt;br /&gt;
|Telefon=07066-9192790&lt;br /&gt;
|Fax=07066-9192791&lt;br /&gt;
|Öffnungszeiten=Mo.-Do. 8.00 - 20.00 Uhr&amp;lt;br&amp;gt;Fr. 8.00 - 12.00 Uhr&amp;lt;br&amp;gt;&lt;br /&gt;
|Weblink=http://www.centralsystems.de&lt;br /&gt;
|Email=info@centralsystems.de&lt;br /&gt;
|Bemerkung=Standardkomponenten vor allem im Bereich der EDV, keine SMD-Teile, keine Microcontroller.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Heilbronn===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Krauss Elektronik GmbH&lt;br /&gt;
|Straße=Turmstraße 20&lt;br /&gt;
|PLZ=74072 &lt;br /&gt;
|Ort=Heilbronn&lt;br /&gt;
|Telefon=07131/68191&lt;br /&gt;
|Fax=07131/68192&lt;br /&gt;
|Öffnungszeiten=Mo.-Mi.+Fr. 9.00 - 18.00 Uhr&amp;lt;br&amp;gt;Do. 9.00 - 19.00 Uhr&amp;lt;br&amp;gt;Sa. 9.00 - 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.krauss-elektronik.de/&lt;br /&gt;
|Email=info@krauss-elektronik.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
===Freiburg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Omega electronic GmbH&lt;br /&gt;
|Straße=Eschholzstr. 58-60&lt;br /&gt;
|PLZ=79115&lt;br /&gt;
|Ort=Freiburg&lt;br /&gt;
|Telefon=0761/76776-0&lt;br /&gt;
|Fax=0761/76776-55&lt;br /&gt;
|Öffnungszeiten=Mo.-Sa.: 10:00 - 19:30&lt;br /&gt;
|Weblink=http://www.omega-electronic.de&lt;br /&gt;
|Email=info@omega-electronic.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
===Karlsbad===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=IT-WNS, Thomas Heldt&lt;br /&gt;
|Straße=Schulstr. 13&lt;br /&gt;
|PLZ=76307&lt;br /&gt;
|Ort=Karlsbad - Mutschelbach&lt;br /&gt;
|Telefon=07202/936083&lt;br /&gt;
|Fax=07202/936085&lt;br /&gt;
|Öffnungszeiten=Nach Vereinbahrung (Email-Kontakt)&lt;br /&gt;
|Weblink=http://www.it-wns.de&lt;br /&gt;
|Email=info@it-wns.de&lt;br /&gt;
|Bemerkung=Bestellungen im Onlineshop können wahlweise auch direkt abgeholt werden&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Karlsruhe===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=mükra electronic shop GmbH&lt;br /&gt;
|Straße=Fritz-Erler-Straße 24&lt;br /&gt;
|PLZ=76133&lt;br /&gt;
|Ort=Karlsruhe&lt;br /&gt;
|Telefon=0721/374270&lt;br /&gt;
|Fax=0721/9379171&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9.00 - 13.00 Uhr, 14.30 - 18.00 Uhr&amp;lt;br&amp;gt;Sa. 9.00 - 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.muekra.com/filiale_karlsruhe.html&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Werner Bremer Elektrotechnik &amp;amp; Einzelhandel&lt;br /&gt;
|Straße=Zähringerstraße 55a&lt;br /&gt;
|PLZ=76133&lt;br /&gt;
|Telefon=&lt;br /&gt;
|Fax=&lt;br /&gt;
|Ort=Karlsruhe&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Bemerkung=Vielleicht geschlossen?? keine Website. Versand?&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Ludwigsburg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Mayer Elektronik&lt;br /&gt;
|Straße=Stuttgarter Str. 32&lt;br /&gt;
|PLZ=71638&lt;br /&gt;
|Ort=Ludwigsburg&lt;br /&gt;
|Telefon=07141 920 711&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 09.00-12.30 und 14.30-18.00 Uhr, Mittwoch nachmittags geschlossen, Sa. 09.00-12:30 Uhr&lt;br /&gt;
|Weblink=http://www.Mayer-Elektronik.de&lt;br /&gt;
|Email=info@Mayer-Elektronik.de&lt;br /&gt;
|Bemerkung= Spezialisiert auf Antennentechnik, Sat-Anlagen. Verschiedene Kleinteile erhältlich&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Mannheim===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad&lt;br /&gt;
|Straße=Morchfeldstr. 37-39&lt;br /&gt;
|PLZ=68199&lt;br /&gt;
|Ort=Mannheim - Neckarau&lt;br /&gt;
|Telefon=0180 6 564445 (20 Cent/Verbindung aus dem Festnetz, max. 60 Cent/Verbindung aus dem Mobilfunknetz)&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 10.00-20.00 Uhr, Sa. 10.00-18.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.de/ce/de/ChainstoreInfo.html?detail&amp;amp;chainstorecode=CS_DE_MA&lt;br /&gt;
|Email=filiale.mannheim@conrad.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Offenburg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Günther Wultschner (Elektronikladen)&lt;br /&gt;
|Straße=Luisenstraße 16&lt;br /&gt;
|PLZ=77654&lt;br /&gt;
|Ort=Offenburg&lt;br /&gt;
|Telefon=0781 43270‎&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9.00 - 19:00 Uhr&amp;lt;br&amp;gt;Sa. 9.00 - 16.00 Uhr&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Pforzheim===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=mükra electronic shop GmbH&lt;br /&gt;
|Straße=Westliche Karl-Friedrich-Str. 73 &lt;br /&gt;
|PLZ=75172 &lt;br /&gt;
|Ort=Pforzheim&lt;br /&gt;
|Telefon=07231 313952&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9.00 - 12:30 Uhr; 14.00 Uhr - 18.00 Uhr&amp;lt;br&amp;gt;Sa. 9.00 - 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.muekra.com/Filiale_Pforzheim&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Reutlingen===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=mükra electronic shop GmbH&lt;br /&gt;
|Straße=Federnseestr. 4 &lt;br /&gt;
|PLZ=72764&lt;br /&gt;
|Ort=Reutlingen&lt;br /&gt;
|Telefon=07121/370748&lt;br /&gt;
|Fax=07121/370741&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9.30 - 13.00 Uhr, 14.00 - 18.00 Uhr&amp;lt;br&amp;gt;Sa. 9.30 - 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.muekra.com/filiale_reutlingen.html&lt;br /&gt;
|Email=info@muekra.de&lt;br /&gt;
|Bemerkung=Keine SMD-Teile&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Schwäbisch Gmünd===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=mükra electronic shop GmbH&lt;br /&gt;
|Straße=Kalter Markt 12&lt;br /&gt;
|PLZ=73525&lt;br /&gt;
|Ort=Schwäbisch Gmünd&lt;br /&gt;
|Telefon=07171/64352&lt;br /&gt;
|Fax=07171/405684&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9.00 - 12.30 Uhr, 14.00 - 18.00 Uhr&amp;lt;br&amp;gt;Sa. 9.30 - 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.muekra.com/Filiale_Schwaebisch_Gmuend&lt;br /&gt;
|Email=info@muekra.de&lt;br /&gt;
|Bemerkung=Keine SMD-Teile&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Stuttgart===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Dräger Electronic &amp;amp; Audio GmbH&lt;br /&gt;
|Straße=Hauptstätter Strasse 55&lt;br /&gt;
|PLZ=70178&lt;br /&gt;
|Ort=Stuttgart&lt;br /&gt;
|Telefon=+49 711 601818-46&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Montag - Freitag  10:00 - 19:00 Uhr Samstag 10:00 - 16:00 Uhr&lt;br /&gt;
|Weblink=http://www.dea-gmbh.de&lt;br /&gt;
|Email=info@draeger-electronic.de&lt;br /&gt;
|Bemerkung= Audio, Hifi, Tontechnik, Kabel, schon lange keine Bauelemente mehr!&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad&lt;br /&gt;
|Straße=Kronenstraße 7, Altes Postquartier&lt;br /&gt;
|PLZ=70173&lt;br /&gt;
|Ort=Stuttgart&lt;br /&gt;
|Telefon=0180 5 564445 (14 Cent/Min. aus dem Festnetz, max. 42 Cent/Min. aus dem Mobilfunknetz)&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Sa. 10.00-20.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.de/ce/de/ChainstoreInfo.html?detail&amp;amp;chainstorecode=CS_DE_S&lt;br /&gt;
|Email=filiale.stuttgart@conrad.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Ulm===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=AW-Elektronik&lt;br /&gt;
|Straße=Im Brühl 9&lt;br /&gt;
|PLZ=89194&lt;br /&gt;
|Ort=Ulm/Schnürpflingen&lt;br /&gt;
|Dienstleistungen=Entwicklung Hard &amp;amp; Software; Leiterplattenbestückung SMD und THD; Leiterplattenlayout und Routing&lt;br /&gt;
|Weblink=http://www.aw-elektronik.de&lt;br /&gt;
|Email=info@aw-elektronik.de&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Telefon=&lt;br /&gt;
|Fax=&lt;br /&gt;
|Bemerkung=Nach Absprache können lagernde Bauteile gekauft werden. Am Lager sind SMD-Bauteile wie z.B. AVR, Widerstände, Kondensatoren, LEDs; Die LEDs sind als WarmWeiß 3500K (1800mcd), Kaltweis 7000K (2850mcd), Rot (1500mcd) und Blau (350mcd) und UV vorhanden.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=mükra electronic shop GmbH&lt;br /&gt;
|Straße=Neutorstr. 20&lt;br /&gt;
|PLZ=89073&lt;br /&gt;
|Ort=Ulm&lt;br /&gt;
|Telefon=0731/64494&lt;br /&gt;
|Fax=0731/6028676&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9.00 - 18.00 Uhr&amp;lt;br&amp;gt;Sa. 9.00 - 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.muekra.com/Filiale_Ulm.html&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=Keine SMD-Teile&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Villingen-Schwenningen===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Buchmann Elektronik&lt;br /&gt;
|Straße=Wasenstraße 51&lt;br /&gt;
|PLZ=78054&lt;br /&gt;
|Ort=Villingen-Schwenningen&lt;br /&gt;
|Telefon=07720/1308&lt;br /&gt;
|Fax=07720/1360&lt;br /&gt;
|Öffnungszeiten=Montag bis Freitag 09.00 - 12.30 &amp;amp; 14.00 - 19.00 Uhr; Mittwoch Nachmittag geschlossen; Samstag geschlossen&lt;br /&gt;
|Weblink=http://www.buchmann-elektronik.de&lt;br /&gt;
|Email=buchmann-elektronik@t-online.de&lt;br /&gt;
|Bemerkung=keine SMD.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=SchiBe Elektronik&lt;br /&gt;
|Straße=Mühlenstr. 9&lt;br /&gt;
|PLZ=78050&lt;br /&gt;
|Ort=Villingen-Schwenningen&lt;br /&gt;
|Telefon=07721/8879880&lt;br /&gt;
|Fax=07721/88798820&lt;br /&gt;
|Öffnungszeiten=Mo-Fr.; Zeiten leider unbekannt&lt;br /&gt;
|Weblink=http://www.schibe.de&lt;br /&gt;
|Email=info@SchiBe.de&lt;br /&gt;
|Bemerkung=Bestellt auf Anfrage ohne Versandkosten bei Conrad mit Selbstabholung dort.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Bayern==&lt;br /&gt;
===Ansbach===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Mehl Electronic&lt;br /&gt;
|Straße=Schaitbergerstr. 1&lt;br /&gt;
|PLZ=91522&lt;br /&gt;
|Ort=Ansbach&lt;br /&gt;
|Telefon=0981/977166&lt;br /&gt;
|Fax=0981/977167&lt;br /&gt;
|Öffnungszeiten=Montag - Freitag 09:00 - 18:00 Uhr&amp;lt;br&amp;gt;Samtag 10:00 - 13:00 Uhr &lt;br /&gt;
|Weblink=http://www.electronic-mehl.de&lt;br /&gt;
|Email=service@electronic-mehl.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Aschaffenburg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=VS Elektronik OHG&lt;br /&gt;
|Straße= Magnolienweg 3&lt;br /&gt;
|PLZ= 63739&lt;br /&gt;
|Ort=Aschaffenburg &lt;br /&gt;
|Telefon=06021-30460&lt;br /&gt;
|Fax=06021-304626&lt;br /&gt;
|Öffnungszeiten= Montag - Freitag: 09:00 - 18:00 Uhr&amp;lt;br&amp;gt;Samstag: 09:00 - 13:00 Uhr&lt;br /&gt;
|Weblink=http://www.vs-electronic.de&lt;br /&gt;
|Email=info@vs-electronic.de&lt;br /&gt;
|Bemerkung= führt Vellemann Bausätze, PCs, Telekommunikation und Bauteile&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Augsburg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=rf-elektronik&lt;br /&gt;
|Straße= Karlstraße 2, Eingang über Obstmarkt&lt;br /&gt;
|PLZ=86150&lt;br /&gt;
|Ort=Augsburg &lt;br /&gt;
|Telefon=0821 39830&lt;br /&gt;
|Fax=0821 518727﻿&lt;br /&gt;
|Öffnungszeiten= Montag - Freitag: 10:00 - 18:00 Uhr&amp;lt;br&amp;gt;Samstag: 10:00 - 13:00 Uhr&lt;br /&gt;
|Weblink=http://www.rf-elektronik.de&lt;br /&gt;
|Email=info@rf-elektronik.de&lt;br /&gt;
|Bemerkung=Bauelemente, Satellitentechnik+Zubehör, Halbleitertechnik, Satellitenanlagenbau, Akkus, im Laden befindet sich als nettes Schmankerl auch ein winziges, technisches Bücher-Antiquariat&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Bamberg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Elektronik Box&lt;br /&gt;
|Straße=Untere Königstraße 29&lt;br /&gt;
|PLZ=96052&lt;br /&gt;
|Ort=Bamberg&lt;br /&gt;
|Telefon=0951 28500&lt;br /&gt;
|Öffnungszeiten= Montag - Freitag: 9:00 - 13:00 Uhr, 14:00 - 18:00 Uhr&amp;lt;br&amp;gt;Samstag: 9:00 - 13:00 Uhr  &lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
‎ &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Erlangen===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Schoffer Radio-Fernseh-Elektronik&lt;br /&gt;
|Straße=Beethovenstraße 2&lt;br /&gt;
|PLZ=91052&lt;br /&gt;
|Ort=Erlangen‎&lt;br /&gt;
|Telefon=09131 25288‎&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Feller-electronic&lt;br /&gt;
|Straße=Marquardsenstraße 21&lt;br /&gt;
|PLZ=91054 &lt;br /&gt;
|Ort=Erlangen‎&lt;br /&gt;
|Telefon=09131 27595&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo-Fr 9.00 - 13.00, 14.00-18.00 Uhr. Sa 9.00 - 14.00 Uhr&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Fürth===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=B+D Electronic GmbH&lt;br /&gt;
|Straße=Königstr. 107 (gegenüber Citycenter und neben Feuerwehr)&lt;br /&gt;
|PLZ=90762 &lt;br /&gt;
|Ort=Fürth&lt;br /&gt;
|Telefon=0911 - 77 30 40&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo - Fr:   10.00 - 12.30 &amp;amp; 14.00 - 18.00&amp;lt;br&amp;gt;Sa:        10.00 - 13.00&lt;br /&gt;
|Weblink=http://www.bdelectronic.de/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= keine Microkontoller&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Holzheim===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=RH Electronic Eva Spaeth&lt;br /&gt;
|Straße=Ostertalstraße 15&lt;br /&gt;
|PLZ=86684&lt;br /&gt;
|Ort=Holzheim&lt;br /&gt;
|Telefon=08276 / 58800&lt;br /&gt;
|Fax=08276 / 58802&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=http://rhelectronic.tradoria.de/&lt;br /&gt;
|Email=eva@peterscable.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Kaufbeuren ===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Jantsch-Elektronik GmbH&lt;br /&gt;
|Straße=Porschestraße  26&lt;br /&gt;
|PLZ=87600&lt;br /&gt;
|Ort=Kaufbeuren&lt;br /&gt;
|Telefon=0 83 41 / 95 33-0&lt;br /&gt;
|Fax=0 83 41 / 37 00&lt;br /&gt;
|Öffnungszeiten=Mo-Fr 9:00-12:30 / 13:30-18:00&amp;lt;br/&amp;gt;&lt;br /&gt;
Sa 9:00-13:00 Uhr&lt;br /&gt;
|Weblink= http://www.j-e.de&lt;br /&gt;
|Email=info@j-e.de&lt;br /&gt;
|Bemerkung=führt auch gebrauchte Messgeräte&lt;br /&gt;
}}&lt;br /&gt;
=== Kiefersfelden (bei Rosenheim) ===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=db-electronic, Jutta Richter&lt;br /&gt;
|Straße=Dorfstr. 30&lt;br /&gt;
|PLZ=83088&lt;br /&gt;
|Ort=Kiefersfelden&lt;br /&gt;
|Telefon=0 80 33 / 86 80&lt;br /&gt;
|Fax=0 80 33 / 76 19&lt;br /&gt;
|Öffnungszeiten=Mo-Fr 9:00-18:00&amp;lt;br/&amp;gt;&lt;br /&gt;
Sa 10:00-12:00 Uhr&lt;br /&gt;
|Weblink= http://www.db-electronic.de&lt;br /&gt;
|Email=info@db-electronic.de&lt;br /&gt;
|Bemerkung=Verkauft neben aktiven, passiven und mechanischen Bauteilen, Werkzeug, technische Sprays, Messgeräte, Kleingeräte, LED-Artikel, Kabel, Akkus, Batterien und Computerzubehör&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Landshut===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Der Elektronik Landen&lt;br /&gt;
|Straße=Innere Münchener Straße 16&lt;br /&gt;
|PLZ=84036&lt;br /&gt;
|Ort=Landshut&lt;br /&gt;
|Öffnungszeiten=Mo - Fr 8:00 - 12:00, 14:00 - 18:00; Sa 8:00 - 12:00&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= kleiner, aber feiner Laden. Verkauft eher nur Analogtechnik, Röhren, und recht selten gewordene Sachen&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Rauschhuber Electronic&lt;br /&gt;
|Straße=Gaußstraße 2&lt;br /&gt;
|PLZ=84030&lt;br /&gt;
|Ort=Landshut&lt;br /&gt;
|Öffnungszeiten=Mo - Fr 8:00 - 17:00&lt;br /&gt;
|Weblink=http://rauschhuber.de/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= Kleiner Laden neben MediaMarkt. Verkauft Verbrauchsmaterial, wie Zinn und Lötsauglitze, Aktive und Passive Bauelemente und Werkzeug, wie Lötkolben, Seitenschneider, Schraubenderher u.n.v.m.K..&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Leiblfing (bei Straubing)===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Hans Entner Funkelektronik&lt;br /&gt;
|Straße=Landshuter Straße 1 &lt;br /&gt;
|PLZ=94339&lt;br /&gt;
|Ort=Leiblfing&lt;br /&gt;
|Telefon=(0 94 27) 90 20 86&lt;br /&gt;
|Fax=09427 - 902087&lt;br /&gt;
|Öffnungszeiten= leider nicht bekannt&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=Entner-DF9RJ@t-online.de&lt;br /&gt;
|Bemerkung= Kleiner Laden und sehr netter Inhaber. Einige Geräte und Zubehör. Viele HF-Stecker (v.a. SMA, BNC, N und PL(UHF)) und Koax-Kabel. Bietet auch Reparaturen an.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===München===&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=AM Elektronik Vertrieb - Albert &amp;amp; Machl KG&lt;br /&gt;
|Straße=Kirchtruderinger Str. 6&lt;br /&gt;
|PLZ=81829&lt;br /&gt;
|Ort= München (Trudering)&lt;br /&gt;
|Telefon=089/4904180 &lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo-Do 8:00-12:00, 13:00-17:00 Uhr, Fr 8:00-12:00, 13:00-15:00 Uhr&lt;br /&gt;
|Weblink=http://stores.ebay.de/weri2elektronikwelt, http://www.am-elektronik.de&lt;br /&gt;
|Email=info@am-elektronik.de&lt;br /&gt;
|Bemerkung=Laden ist eher ein grosses Lager, daher am besten Bestellung per Mail und dann abholen.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Tal 29&lt;br /&gt;
|PLZ=80331 &lt;br /&gt;
|Ort=München&lt;br /&gt;
|Telefon=&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo - Fr:   9:00 - 20:00&amp;lt;br&amp;gt;Sa:        9:00 - 20:00&lt;br /&gt;
|Weblink=http://www.conrad.de/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Hanauer Straße 91 (gegenüber OEZ)&lt;br /&gt;
|PLZ=80993 &lt;br /&gt;
|Ort=München - Moosach&lt;br /&gt;
|Telefon=&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo - Fr:   9:30 - 20:00&amp;lt;br&amp;gt;Sa:        9:00 - 20:00&lt;br /&gt;
|Weblink=http://www.conrad.de/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Bürklin&lt;br /&gt;
|Straße=Grünwalder Weg 30&lt;br /&gt;
|PLZ=82041 &lt;br /&gt;
|Ort=Oberhaching&lt;br /&gt;
|Telefon=(089) 55 875-0&lt;br /&gt;
|Fax=(089) 55 875-421&lt;br /&gt;
|Öffnungszeiten=Mo - Do:   9:00 - 16:30&amp;lt;br&amp;gt;Fr:        9:00 - 13:00&lt;br /&gt;
|Weblink=http://buerklin.de/&lt;br /&gt;
|Email=info@buerklin.de&lt;br /&gt;
|Bemerkung= Achtung neuer Standort nicht mehr Schillerstraße !!!&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Strixner &amp;amp; Holzinger&lt;br /&gt;
|Straße=Schillerstraße 25-29&lt;br /&gt;
|PLZ=80336 &lt;br /&gt;
|Ort=München&lt;br /&gt;
|Telefon=&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo - Fr:   9:30 - 18:00&lt;br /&gt;
|Weblink=http://www.strixner-electronic.de, http://sh-halbleiter.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Nürnberg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Fürther Straße 212&lt;br /&gt;
|PLZ=90429&lt;br /&gt;
|Ort=Nürnberg&lt;br /&gt;
|Telefon=0180 5 564445&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo - Fr:   9:30 - 20:00&amp;lt;br&amp;gt;Sa: 9.30 - 18:00&lt;br /&gt;
|Weblink=http://www.conrad.de/&lt;br /&gt;
|Email=filiale.nuernberg@conrad.de&lt;br /&gt;
|Bemerkung=Gegenüber ehemaligem Quelle-Stammhaus&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Rauch Elektronik&lt;br /&gt;
|Straße=Ehemannstraße 7&lt;br /&gt;
|PLZ=90478&lt;br /&gt;
|Ort=Nürnberg&lt;br /&gt;
|Telefon=0911469224&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Regensburg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Langobardenstraße 2&lt;br /&gt;
|PLZ=93053&lt;br /&gt;
|Ort=Regensburg&lt;br /&gt;
|Telefon=0180 5 564445&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo - Sa:   9:30 - 20:00&lt;br /&gt;
|Weblink=http://www.conrad.de/&lt;br /&gt;
|Email=filiale.regensburg@conrad.de&lt;br /&gt;
|Bemerkung=Im Fachmarktzentrum Bajuwarenstraße&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Schweinfurt===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Spath Elektronische Bauteile&lt;br /&gt;
|Straße=Cramerstr. 9&lt;br /&gt;
|PLZ=97421&lt;br /&gt;
|Ort=Schweinfurt&lt;br /&gt;
|Telefon=09721/25186&lt;br /&gt;
|Fax=09721/22999&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Straubing===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Röhrner Electronic&lt;br /&gt;
|Straße=Innere Passauer Str. 12&lt;br /&gt;
|PLZ=94315&lt;br /&gt;
|Ort=Straubing&lt;br /&gt;
|Telefon=09421/12573&lt;br /&gt;
|Fax=09421/22207&lt;br /&gt;
|Öffnungszeiten=Mo - Do:   9:00 - 18:00&amp;lt;br&amp;gt;Sa:        10:00 - 13:00&lt;br /&gt;
|Weblink=http://www.roehrner-electronic.de/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=Netter Elektronikladen mit vielen Halbleitern neben dem üppigen Standardsortiment&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Pförring===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Pollin&lt;br /&gt;
|Straße=Max-Pollin-Straße 1&lt;br /&gt;
|PLZ=85104&lt;br /&gt;
|Ort=Pförring&lt;br /&gt;
|Telefon=&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9 - 19 Uhr&amp;lt;br&amp;gt;Sa. 9 - 16 Uhr &lt;br /&gt;
|Weblink=http://www.pollin.de/shop/static/ecenter.htm&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=nähe Ingolstadt&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Berlin==&lt;br /&gt;
===Charlottenburg-Wilmersdorf===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Segor-electronics&lt;br /&gt;
|Straße=Kaiserin-Augusta-Allee 94&lt;br /&gt;
|PLZ=10589&lt;br /&gt;
|Ort=Berlin&lt;br /&gt;
|Telefon=030 4399843&lt;br /&gt;
|Fax=030 4399855&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 10.00-13.30 Uhr und 14:30-18:00 Uhr, Sa. 10.00-13.00 Uhr&lt;br /&gt;
|Weblink=http://www.segor.de&lt;br /&gt;
|Email=sales@segor.de&lt;br /&gt;
|Bemerkung=Sehr gut sortiertes und vielseitiges Sortiment, preiswert, hochwertig.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Kreuzberg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Hasenheide 14-15&lt;br /&gt;
|PLZ=10967&lt;br /&gt;
|Ort=Berlin&lt;br /&gt;
|Telefon=0180 5 564445 &lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 10.00-20.00 Uhr, Sa. 10.00-18.00 Uhr &lt;br /&gt;
|Weblink=http://www.conrad.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Friedrichshain===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=TinkerSoup&lt;br /&gt;
|Straße=Frankfurter Allee 53&lt;br /&gt;
|PLZ=10247&lt;br /&gt;
|Ort=Berlin&lt;br /&gt;
|Telefon=03077903156&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=http://www.tinkersoup.de&lt;br /&gt;
|Email=info@tinkersoup.de&lt;br /&gt;
|Bemerkung=Ist ein reiner Onlineshop eigentlich, bieten aber Selbstabholung nach Absprache an, eingetragene Adresse ist wohl für Selbstabholer&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Prenzlauer Berg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=SLY electronic&lt;br /&gt;
|Straße=Erich-Weinert-Straße 139-141&lt;br /&gt;
|PLZ=10409&lt;br /&gt;
|Ort=Berlin&lt;br /&gt;
|Telefon=030 428492-0&lt;br /&gt;
|Fax=030 428492-29&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 10.00-20.00 Uhr, Sa. 10.00-16.00 Uhr&lt;br /&gt;
|Weblink=http://www.sly.de&lt;br /&gt;
|Email=mail@sly.de&lt;br /&gt;
|Bemerkung=Sehr begrenztes Angebot, Internetseite nicht aussagekräftig (was den Bestand angeht) und nicht gerade günstig.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Schöneberg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Kleiststraße 30-31&lt;br /&gt;
|PLZ=10787&lt;br /&gt;
|Ort=Berlin&lt;br /&gt;
|Telefon=0180 5 564445 &lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Sa. 10.00-20.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Atzert Radio&lt;br /&gt;
|Straße=Kleiststraße 32-33&lt;br /&gt;
|PLZ=10787&lt;br /&gt;
|Ort=Berlin&lt;br /&gt;
|Telefon=030/212984-0 &lt;br /&gt;
|Fax=030/212984-11&lt;br /&gt;
|Öffnungszeiten=Montag-Samstag 10:00-19.00 Uhr&lt;br /&gt;
|Weblink=http://www.atzert-radio.de/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Steglitz===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Schloßstraße 34-36&lt;br /&gt;
|PLZ=12163&lt;br /&gt;
|Ort=Berlin&lt;br /&gt;
|Telefon=0180 5 564445 &lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Do. 10.00-20.00 Uhr, Fr.-Sa. 10.00-22.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Brandenburg==&lt;br /&gt;
&lt;br /&gt;
===Cottbus===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Klauck Electronic-Shop&lt;br /&gt;
|Straße=Karl-Liebknecht-Str. 53a&lt;br /&gt;
|PLZ=03046&lt;br /&gt;
|Ort=Cottbus&lt;br /&gt;
|Telefon=0355 / 797044&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=reichlich eigene Parkplätze im Hof&lt;br /&gt;
|&lt;br /&gt;
|Mai 2014: seit längerem geschlossen :(&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=electronic Shop cottbus&lt;br /&gt;
|Straße=Schweriner Str. 2&lt;br /&gt;
|PLZ=03046&lt;br /&gt;
|Ort=Cottbus&lt;br /&gt;
|Telefon=0355 / 2890585&lt;br /&gt;
|Fax=0355 / 539545&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr.  09.00 - 12.00 Uhr, 14.00 - 18.00 Uhr&lt;br /&gt;
|Weblink=http://www.eshop-cb.de/eshop/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=der Shop ist sehr jung, daher ist das Team noch etwas unerfahren, macht dies aber durch Freundlichkeit wett&lt;br /&gt;
|Von der Straße aus nicht auszumachen, am besten zu Fuß erkunden. Mai 2014: Eintrag noch immer aktuell&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Koenig Elektronik / Technorm&lt;br /&gt;
|Straße=Karl-Liebknecht-Str. 59&lt;br /&gt;
|PLZ=03046&lt;br /&gt;
|Ort=Cottbus&lt;br /&gt;
|Telefon=0355 22746&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 08.00 - 14.00 Uhr&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=ein &amp;quot;typischer Krämerladen&amp;quot;, hat aber genau das, was man vom lokalen Händer erwartet.&lt;br /&gt;
|richtet sich eher an gewerbliche Radiotechniker, verkauft aber problemlos auch an Endkunden.&lt;br /&gt;
|Laden im Hinterhof. Mai 2014: Eintrag noch aktuell&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Frankfurt (Oder)===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Elektronik Service Landrock&lt;br /&gt;
|Straße=Karl-Ritter-Platz 8-9&lt;br /&gt;
|PLZ=15230&lt;br /&gt;
|Ort=Frankfurt (Oder) &lt;br /&gt;
|Telefon=0335 / 6802029&lt;br /&gt;
|Fax=0335 / 684171&lt;br /&gt;
|Öffnungszeiten=Mo-Fr. 10-18 Uhr&lt;br /&gt;
|Weblink=http://www.landrock-elektronik.de/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=Jürgen (Chef) ist superfreundlich, habe jahrelang dort eingekauft.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Bremen==&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Borgwardstr. 2&lt;br /&gt;
|PLZ=28279&lt;br /&gt;
|Ort=Bremen &lt;br /&gt;
|Telefon=01 80 / 55 64 44 5&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=http://www.conrad.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Schuricht Distrelec GmbH&lt;br /&gt;
|Straße=Rehland 8&lt;br /&gt;
|PLZ=28832&lt;br /&gt;
|Ort=Achim &lt;br /&gt;
|Telefon= 04 20 2 / 97 47-97&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=https://www.distrelec.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= Nur telefonische Bestellung. Wenn man bei der Bestellung explizit sagt dass man die Sachen in Achim abholen möchte, dann klappt dies auch... meistens...&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Hamburg==&lt;br /&gt;
=== Hammerbrook ===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Radio Kölsch&lt;br /&gt;
|Straße=Kreuzbrook 14&lt;br /&gt;
|PLZ=20537&lt;br /&gt;
|Ort=Hamburg &lt;br /&gt;
|Telefon=040 / 653 00 81&lt;br /&gt;
|Fax=040 / 653 00 80&lt;br /&gt;
|Öffnungszeiten=Montag - Freitag 10:00 - 19:00 Uhr, Samstag 10:00 - 16:00 Uhr&lt;br /&gt;
|Weblink=http://www.shop-koelsch24.com/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Hoheluft Ost===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Statronic&lt;br /&gt;
|Straße=Hoheluftchaussee 84&lt;br /&gt;
|PLZ=20253&lt;br /&gt;
|Ort=Hamburg &lt;br /&gt;
|Telefon=040 / 422 33 22&lt;br /&gt;
|Fax=040 / 422 33 25&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=http://www.statronic.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=Stand 06/2014 kaum noch Bauteile verfügbar, mit Chance bekommt man einfache Teile z.B. Schraubklemmblöcke. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Wandsbek===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Wandsbeker Zollstr. 67-69&lt;br /&gt;
|PLZ=22041&lt;br /&gt;
|Ort=Hamburg&lt;br /&gt;
|Telefon=0180 5 564445 &lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 09.30-20.00 Uhr, Sa. 09.00-18.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Altona===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Hahnenkamp 1&lt;br /&gt;
|PLZ=22765 &lt;br /&gt;
|Ort=Hamburg&lt;br /&gt;
|Telefon=0180 5 564445 &lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Sa. 10.00-20.00 Uhr &lt;br /&gt;
|Weblink=http://www.conrad.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Hessen==&lt;br /&gt;
===Darmstadt===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Zimmermann Electronic GmbH&lt;br /&gt;
|Straße=Kasinostr. 2&lt;br /&gt;
|PLZ=64293 &lt;br /&gt;
|Ort=Darmstadt&lt;br /&gt;
|Telefon=06151 - 66 69 - 240&lt;br /&gt;
|Fax=06151 - 66 69 - 290&lt;br /&gt;
|Öffnungszeiten=Mo.- Fr. 9:00 - 19:00 Uhr&amp;lt;br&amp;gt;Sa. 9:00 - 14:00 Uhr&lt;br /&gt;
|Weblink=http://www.zeg-shop.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=EBG Electronic Business GmbH&lt;br /&gt;
|Straße=Bismarckstr. 61&lt;br /&gt;
|PLZ=64293 &lt;br /&gt;
|Ort=Darmstadt&lt;br /&gt;
|Telefon=06151 / 82 91 - 0&lt;br /&gt;
|Fax=06151 / 82 91 - 20&lt;br /&gt;
|Öffnungszeiten=Montag-Freitag: 9:00 bis 18:00 Uhr&amp;lt;br&amp;gt;Samstag:        9:00 bis 13:00 Uhr&lt;br /&gt;
|Weblink=http://www.ebg-darmstadt.de&lt;br /&gt;
|Email=info@ebg-darmstadt.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Frankfurt/Main===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Zeil 58 + 64 (Konstabler Wache)&lt;br /&gt;
|PLZ=60313 &lt;br /&gt;
|Ort=Frankfurt&lt;br /&gt;
|Telefon=0180 5 564445&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Mi. 10.00-20.00 Uhr&amp;lt;br&amp;gt;Do.-Sa. 10.00-21.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Hanau===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=MP Elektronik Vertriebs GmbH&lt;br /&gt;
|Straße=Hospitalstr. 13&lt;br /&gt;
|PLZ=63450 &lt;br /&gt;
|Ort=Hanau&lt;br /&gt;
|Telefon=06181/253077&lt;br /&gt;
|Fax=06181/921450 &lt;br /&gt;
|Öffnungszeiten= Seit 30. Nov. 2013 geschlosssen wegen jahrelanger unzumutbarer Baustellen in Hanau&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Kassel===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Geddert-Elektronik        (seit Januar 2014 geschlossen)&lt;br /&gt;
|Straße=Holländische Straße 31&lt;br /&gt;
|PLZ=34127&lt;br /&gt;
|Ort=Kassel&lt;br /&gt;
|Telefon=0561 / 897177&lt;br /&gt;
|Fax=0561 / 84329&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= Gibt es leider nicht mehr&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=UK-Elektronik&lt;br /&gt;
|Straße=Königstor 52&lt;br /&gt;
|PLZ=34117&lt;br /&gt;
|Ort=Kassel&lt;br /&gt;
|Telefon=0561 / 771074&lt;br /&gt;
|Fax=0561 / 778373&lt;br /&gt;
|Öffnungszeiten=Montag - Freitag 9:30 bis 18:00 Uhr, Samstag 9:30 bis 13:00 Uhr&lt;br /&gt;
|Weblink=http://www.uk-elektronik.com/&lt;br /&gt;
|Email=info@UK-Elektronik.com&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Schuro Elektronik GmbH&lt;br /&gt;
|Straße=Friedrich-Ebert-Straße 3&lt;br /&gt;
|PLZ=34117&lt;br /&gt;
|Ort=Kassel&lt;br /&gt;
|Telefon=0561 / 16415&lt;br /&gt;
|Fax=0561 / 770318&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= keine Abholung von Bauteilen möglich&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=NT-Elektronik Toprakci Nihat Elektronik&lt;br /&gt;
|Straße=Hoffmann-von-Fallersleben-Str 3&lt;br /&gt;
|PLZ=34117&lt;br /&gt;
|Ort=Kassel&lt;br /&gt;
|Telefon=0561 / 2020858&lt;br /&gt;
|Fax=0561 / 2020857&lt;br /&gt;
|Öffnungszeiten=Montag - Freitag 10:00 – 19:00 Uhr, Samstag 10:00 – 16:00 Uhr&lt;br /&gt;
|Weblink=http://www.nt-elektronik.de/&lt;br /&gt;
|Email=NT-Elektronik@hotmail.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Offenbach===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=rail- electronic GmbH&lt;br /&gt;
|Straße=Waldemar- Klein- Platz 1&lt;br /&gt;
|PLZ=63071&lt;br /&gt;
|Ort=Offenbach&lt;br /&gt;
|Telefon=069 / 88 20 72&lt;br /&gt;
|Fax=069 / 88 31 14&lt;br /&gt;
|Öffnungszeiten=Mo - Fr 10h - 19h&amp;lt;br&amp;gt;Sa 10h - 16h&lt;br /&gt;
|Weblink=http://www.rail-electronic.de&lt;br /&gt;
|Email=info@rail-electronic.de&lt;br /&gt;
|Bemerkung=Computer- Hardware/ Software/ Service/ Kabel/ Stecker/ Adapter/ ... Unser Ladengeschäft befindet sich direkt im Stadion in der unteren Ladenzeile, Sie können gleich davor parken&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Wetzlar===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Electronic-Shop Lutz Hoffmann&lt;br /&gt;
|Straße=Silhöfertorstr. 3&lt;br /&gt;
|PLZ=35578&lt;br /&gt;
|Ort=Wetzlar&lt;br /&gt;
|Telefon=06441 / 94627&lt;br /&gt;
|Fax=06441 / 946271&lt;br /&gt;
|Öffnungszeiten=Mo. - Sa. 9.00 - 13.00 Uhr&amp;lt;br&amp;gt;Mo. - Fr. 14.00 - 18.00 Uhr&lt;br /&gt;
|Weblink=http://www.funk-shop.de/&lt;br /&gt;
|Email=mail@funk-shop.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Wöllstadt===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=ELW Elektronik Handels GMBH&lt;br /&gt;
|Straße=Am Kalkofen 10&lt;br /&gt;
|PLZ=61206&lt;br /&gt;
|Ort=Wöllstadt&lt;br /&gt;
|Telefon=06034-4411&lt;br /&gt;
|Fax=06034-5739&lt;br /&gt;
|Öffnungszeiten=Mo. - Fr. 08.30 - 18.00 Uhr&amp;lt;br&amp;gt;Sa. 08.30 - 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.elw-elektronik.com/&lt;br /&gt;
|Email=elw-gmbh@t-online.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Mecklenburg-Vorpommern==&lt;br /&gt;
===Rostock===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=A-Z City-Stores&lt;br /&gt;
|Straße=Doberaner-Hof&lt;br /&gt;
|PLZ=&lt;br /&gt;
|Ort=Rostock&lt;br /&gt;
|Telefon=0381-4031171&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo. – Fr.    9.30 – 19.30 Uhr&amp;lt;br&amp;gt;Sa.   9.30 – 16.00 Uhr&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=geringes Angebot&amp;lt;br&amp;gt;recht teuer&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Niedersachsen==&lt;br /&gt;
===Braunschweig===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic GmbH&lt;br /&gt;
|Straße=Sudetenstr. 4&lt;br /&gt;
|PLZ=38114&lt;br /&gt;
|Ort=Braunschweig&lt;br /&gt;
|Telefon=0180 5 564445&lt;br /&gt;
|Fax=???&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 09.30-19.00 Uhr, Sa. 09.00-18.00 Uhr (Abweichend in der Vorweihnachtszeit!)&lt;br /&gt;
|Weblink=http://www.filialen.conrad.de/&lt;br /&gt;
|Email=filiale.braunschweig@conrad.de&lt;br /&gt;
|Bemerkung= Die Resterampe hinter dem Hauptgebäude gibt es nicht mehr. Dorthin wurde die Modellbau-Abteilung ausgelagert.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=ETT - Electronic Toys Trading GmbH&lt;br /&gt;
|Straße=Kreuzstraße 65&lt;br /&gt;
|PLZ=38118&lt;br /&gt;
|Ort=Braunschweig&lt;br /&gt;
|Telefon=0531-58 11 00&lt;br /&gt;
|Fax=0531-58 11 030&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=http://www.ett-online.de/&lt;br /&gt;
|Email=bestell@ett-online.de&lt;br /&gt;
|Bemerkung= Online-Katalog unter http://www.megakick-stores.de/. Zu Atzert-Elektronik mutiert. &#039;&#039;&#039;Insolvent!:&#039;&#039;&#039; Stand August 2014: ETT hat Insolvent angemeldet; evtl. Garantieansprüche für alte Käufe werden damit wohl verfallen; sollte das Unternehmen komplett insolvent sein, gilt dies auch für neue Käufe. Auch die angeschlossenen vier Atzert-Läden sind betroffen: alle vier sind für immer geschlossen worden.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Elektronik Bauteile Vertrieb - Dipl.-Ing. Jörg Bassenberg&lt;br /&gt;
|Straße=Nußbergstraße 9&lt;br /&gt;
|PLZ=38102&lt;br /&gt;
|Ort=Braunschweig&lt;br /&gt;
|Telefon=0531-79 17 07&lt;br /&gt;
|Fax=0531-7 60 22&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=http://www.bassenberg.de/&lt;br /&gt;
|Email=info@bassenberg.de&lt;br /&gt;
|Bemerkung= Kleines Ladengeschäft, hauptsächlich ältere Bauteile vorrätig.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=ChiliTec GmbH&lt;br /&gt;
|Straße=Bäckerberg 12&lt;br /&gt;
|PLZ=38165&lt;br /&gt;
|Ort=Lehre-Essenrode&lt;br /&gt;
|Telefon=05301-9029980&lt;br /&gt;
|Fax=05301-9029988&lt;br /&gt;
|Öffnungszeiten=Mo-Fr 8-17 Uhr&lt;br /&gt;
|Weblink=http://www.chilitec.de/&lt;br /&gt;
|Email=willkommen@chilitec.de&lt;br /&gt;
|Bemerkung= Elektronik-Großhandel mit Schwerpunkt auf LED-Beleuchtung und Haustechnik. Versand oder Selbstabholung möglich.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Buxtehude===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Sell-Elektronik&lt;br /&gt;
|Straße=Brillenburgsweg 31A&lt;br /&gt;
|PLZ=21614&lt;br /&gt;
|Ort=Buxtehude&lt;br /&gt;
|Telefon=04161-88305&lt;br /&gt;
|Fax=-&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 10:00-12:00 Uhr &amp;amp; 15:00-18:30 Uhr, Sa. 10:00-13:00 Uhr &lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=Ladengeschäft. Riesen Sortiment an passiven, aktiven und mechanischen Bauteilen. Gute Beratung und faire Preise. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Göttingen===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Göttinger Elektronik Center&lt;br /&gt;
|Straße=Bahnhofstr. 9&lt;br /&gt;
|PLZ=37130&lt;br /&gt;
|Ort=Gleichen&lt;br /&gt;
|Telefon=0551-487988&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=Ortsteil Diemarden&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Hannover===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic GmbH&lt;br /&gt;
|Straße=Goseriede 3&lt;br /&gt;
|PLZ=30159&lt;br /&gt;
|Ort=Hannover&lt;br /&gt;
|Telefon= 0180 5 564445&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Sa. 09.00-20.00 Uhr&lt;br /&gt;
|Weblink=http://www.filialen.conrad.de/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Radio Menzel-Electronic&lt;br /&gt;
|Straße=Fössestraße 6&lt;br /&gt;
|PLZ=30451&lt;br /&gt;
|Ort=Hannover&lt;br /&gt;
|Telefon= 0511 442607&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo-Fr: 10:00-13:30, 14:30-18:00  Sa: 10:00-13:30&lt;br /&gt;
|Weblink=http://www.menzel-electronic.de &lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Zloch-Elektronik&lt;br /&gt;
|Straße=Calenberger Str. 33 &lt;br /&gt;
|PLZ=30169&lt;br /&gt;
|Ort=Hannover&lt;br /&gt;
|Telefon=0511 15575 &lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo-Fr: ? Sa: ?&lt;br /&gt;
|Weblink=keine&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= Zloch Elektronik ist einer der ältesten Läden dieser Art in Hannover. Man bekommt dort auch Bauteile, die selten sind (Restbestände alter Zeiten). Anfahrt mit öffentlichen Verkehrsmitteln: Haltestelle Humboldtstraße oder Haltestelle Waterloo. Seit mindestens September 2013 vorübergehend geschlossen (laut Schild). Seit mindestens August 2014 wird Inventar in Umzugskisten verpackt. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Lüneburg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Beusch Elektronik&lt;br /&gt;
|Straße=Reichenbachstr. 8&lt;br /&gt;
|PLZ=21335 &lt;br /&gt;
|Ort=Lüneburg&lt;br /&gt;
|Telefon=04131 33311&lt;br /&gt;
|Fax=?&lt;br /&gt;
|Öffnungszeiten=Mo und Sa geschlossen, Di - Fr 10:00-13:00, 14:00-18:00&lt;br /&gt;
|Bemerkung=Gibt&#039;s nicht mehr! :-(&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=EleConT - Peter Schoss&lt;br /&gt;
|Straße=Schierenweg 29a&lt;br /&gt;
|PLZ=21382&lt;br /&gt;
|Ort=Brietlingen&lt;br /&gt;
|Telefon=04133 404843&lt;br /&gt;
|Fax=?&lt;br /&gt;
|Öffnungszeiten=nach Absprache&lt;br /&gt;
|Weblink=http://www.elecont.de/&lt;br /&gt;
|Email=info@elecont.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Oldenburg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=ebc Utz Kohl GmbH &lt;br /&gt;
|Straße=Alexanderstraße 31&lt;br /&gt;
|Telefon=0441 82114&lt;br /&gt;
|Fax=0441 85801&lt;br /&gt;
|Weblink=www.e-b-c-elektronik.de&lt;br /&gt;
|Email=kontakt@e-b-c-elektronik.de&lt;br /&gt;
|PLZ=26121&lt;br /&gt;
|Ort=Oldenburg&lt;br /&gt;
|Öffnungszeiten=Mo. - Fr. 9:00 - 12:30 und 13:30 - 18:00 Uhr, Sa. 9:00 - 13:00 Uhr&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Osnabrück===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Heinicke-electronic&lt;br /&gt;
|Straße=Meller Str. 43 &lt;br /&gt;
|PLZ=49084&lt;br /&gt;
|Ort=Osnabrück&lt;br /&gt;
|Telefon=0541 587666&lt;br /&gt;
|Fax=0541 586614 &lt;br /&gt;
|Öffnungszeiten=Mo-Fr. 9:30-13:00Uhr und 14:30-18:00Uhr Sa. 9:30-13:00Uhr&lt;br /&gt;
|Weblink=http://www.heinicke-electronic.de/&lt;br /&gt;
|Email=sales@heinicke-electronic.de&lt;br /&gt;
|Bemerkung=Haben auch einen PC Shop nebenan.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Wilhelmshaven===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Reichelt Elektronik&lt;br /&gt;
|Straße=Elektronikring 1 &lt;br /&gt;
|PLZ=26452&lt;br /&gt;
|Ort=Sande&lt;br /&gt;
|Telefon=04422-955 333&lt;br /&gt;
|Fax=04422-955 111 &lt;br /&gt;
|Öffnungszeiten=Montag - Donnerstag: 9:00 - 17:00 Uhr; Freitag: 9:00 - 15:30 Uhr&lt;br /&gt;
|Weblink=http://www.reichelt.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Nordrhein-Westfalen==&lt;br /&gt;
===Aachen===&lt;br /&gt;
Übernommen aus [http://aachen.wikia.com/wiki/Elektronik-Teile http://aachen.wikia.com/wiki/Elektronik-Teile]. Die Liste dort ist größer.&lt;br /&gt;
* TH-Elektronic, Karlsgraben 47, 52062 Aachen, Tel: 404593, Fax: 404594, http://www.th-electronic.de/ Öffnungszeiten Mo-Fr 9-19:30 Sa 9-16 Uhr&lt;br /&gt;
* AG Elektronik Witte &amp;amp; von der Heide, Hirschgraben 9-11, 52062 Aachen, Tel.: 0241-25226. (Sehr freundlich und großes Sortiment, Werktags geöffnet bis 18.30 (genauere Zeiten folgen noch))&lt;br /&gt;
* Helmut Singer Elektronik, Feldchen 16-24, D-52070 Aachen. http://www.singer-elektronik.de/ (Gebrauchte Messgeräte)&lt;br /&gt;
* Zilles Elektronik GmbH, Bauelemente für die Elektronik, Würselener Str. 8, 52080 Aachen (Aachen-Haaren), Tel: 0241-162745&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nähere Umgebung:&#039;&#039;&#039;&lt;br /&gt;
* Fleu Elektronik, Kantgasse 26, 52477 Alsdorf, Tel. 02404/22240, ubestätigt: Öffnungszeiten Mo - Fr. von 09 - 12 und 15 - 19 Uhr.&lt;br /&gt;
* Zilles Elektronik GmbH, Bauelemente für die Elektronik, Aachener Str. 415, 41069 Mönchengladbach, Tel: 02161-176005&lt;br /&gt;
&lt;br /&gt;
===Bielefeld===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=alpha electronic Ing. A. Berger GmbH&lt;br /&gt;
|Straße=Oldentruper Str. 104&lt;br /&gt;
|PLZ=33604&lt;br /&gt;
|Ort=Bielefeld&lt;br /&gt;
|Telefon=0521-324333&lt;br /&gt;
|Fax=0521-320435&lt;br /&gt;
|Öffnungszeiten=Mo. – Sa.    9.00 – 13.00 Uhr&amp;lt;br&amp;gt;Mo. – Fr.   14.00 – 18.00 Uhr&lt;br /&gt;
|Weblink=http://www.alphaelectronic.de/&lt;br /&gt;
|Email=info@alphaelectronic.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Fuchs und Lützow Elekronik - Handelsges. mbH&lt;br /&gt;
|Straße=Heeper Str. 184&lt;br /&gt;
|PLZ=33607&lt;br /&gt;
|Ort=Bielefeld&lt;br /&gt;
|Telefon=0521-5576555&lt;br /&gt;
|Fax=0521-5576557&lt;br /&gt;
|Öffnungszeiten=Mo. – Sa.    9.00 – 13.00 Uhr&amp;lt;br&amp;gt;Mo. – Fr.   14.00 – 18.00 Uhr&lt;br /&gt;
|Weblink=http://www.electronicfuchs.com/&lt;br /&gt;
|Email=info@electronicfuchs.com&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Bochum===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Schmitt Elektronik&lt;br /&gt;
|Straße=Dorstener Straße 18-20&lt;br /&gt;
|PLZ=44787&lt;br /&gt;
|Ort=Bochum&lt;br /&gt;
|Telefon=0234 9620130 &lt;br /&gt;
|Fax=0234 9629132 &lt;br /&gt;
|Öffnungszeiten=Mo - Fr 09:00 - 18:00, Sa 09:00 - 14:00&lt;br /&gt;
|Weblink=http://www.elektronik-wunderland.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=Alteingesessener laden, hat viel seltenes zeug aus den 70-90ern, sortiment an modernen bauteilen wie AVR eher gering. Sehr zu empfehlen.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Bonn===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Karlstraße 3&lt;br /&gt;
|PLZ=53115&lt;br /&gt;
|Ort=Bonn&lt;br /&gt;
|Telefon=0180 5 564445&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Sa. 10.00-20.00 Uhr &lt;br /&gt;
|Weblink=http://www.filialen.conrad.de/rubriken/filialen.php?filiale=28&lt;br /&gt;
|Email=filiale.bonn@conrad.de  &lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=CSD-Electronics&lt;br /&gt;
|Straße=Bunsenstraße 3&lt;br /&gt;
|PLZ=53121&lt;br /&gt;
|Ort=Bonn&lt;br /&gt;
|Telefon=0228 85041574&lt;br /&gt;
|Fax=0228 85041600&lt;br /&gt;
|Öffnungszeiten=offiziell derzeit nur nach Vereinbarung&lt;br /&gt;
|Weblink=http://www.csd-electronics.de/200/cgi-bin/shop.dll?AnbieterID=2&lt;br /&gt;
|Email=support@csd-electronics.de&lt;br /&gt;
|Bemerkung=Artikel können online bestellt und im Laden abgeholt werden&lt;br /&gt;
}}&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=P &amp;amp; M Elektronik Bonn GmbH&lt;br /&gt;
|Straße=Budapester Straße 6&lt;br /&gt;
|PLZ=53111&lt;br /&gt;
|Ort=Bonn&lt;br /&gt;
|Telefon=0228-656005&lt;br /&gt;
|Fax=0228-656336&lt;br /&gt;
|Öffnungszeiten=Mo-Fr 09:00 – 19:00 Uhr&amp;lt;br&amp;gt;Sa 10:00 – 16:00 Uhr&lt;br /&gt;
|Weblink=http://www.pm-elektronik-bonn.de/&lt;br /&gt;
|Email=pm-elektronikbonn@web.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Dortmund===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Westenhellweg 95-101&lt;br /&gt;
|PLZ=44137&lt;br /&gt;
|Ort=Dortmund&lt;br /&gt;
|Telefon=01805-564445 &lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo. – Fr.    10.00 – 20.00 Uhr&amp;lt;br&amp;gt;Sa.  9.30 – 20.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=direkt in der Innenstadt&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=SR-Tronic&lt;br /&gt;
|Straße=Beratgerstr. 28&lt;br /&gt;
|PLZ=44149&lt;br /&gt;
|Ort=Dortmund&lt;br /&gt;
|Telefon=0231-33671-0&lt;br /&gt;
|Fax=0231-33671-25&lt;br /&gt;
|Öffnungszeiten=Mo. – Fr.    10.00 – 18.00 Uhr&lt;br /&gt;
|Weblink=http://www.sr-tronic.de&lt;br /&gt;
|Email=info@sr-tronic.de&lt;br /&gt;
|Bemerkung=Ist zwar ein Versandhandel, Abholung ist aber möglich, extrem kleines Elektroniksortiment (Atmel, Eproms), ist eigentlich ein Satshop. Hat bereits Insolvenz angemeldet.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- laut Diskussion:Lokale_Elektroniklieferanten gibt es den Laden nicht mehr.&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Electronic am Wall&lt;br /&gt;
|Straße=Hoher Wall 22&lt;br /&gt;
|PLZ=44137&lt;br /&gt;
|Ort=Dortmund&lt;br /&gt;
|Telefon=0231-16863&lt;br /&gt;
|Fax= 	0231-160632&lt;br /&gt;
|Öffnungszeiten=Mo. – Fr.    9.00 – 18.00 Uhr (?)&lt;br /&gt;
|Weblink=http://electronic-am-wall.de&lt;br /&gt;
|Email=electronic-am-wall@t-online.de&lt;br /&gt;
|Bemerkung=Alteingesessener Laden mit mittlerem Elektronik und Bauteilesortiment (Leider nicht mehr so viel wie früher).&lt;br /&gt;
}}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Duisburg ===&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Elektro Urban&lt;br /&gt;
|Weblink=http://www.elur.de/&lt;br /&gt;
|Bemerkung= Verzogen nach Oberhausen, Siegesstrasse. Siehe dort.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Düsseldorf ===&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Oststraße 34&lt;br /&gt;
|PLZ=40211&lt;br /&gt;
|Ort=Düsseldorf&lt;br /&gt;
|Telefon=0211 - 38 83 76 - 0&lt;br /&gt;
|Fax=0211 - 38 83 76 - 14&lt;br /&gt;
|Öffnungszeiten=Mo.-Sa. 9.30-20.00 Uhr&lt;br /&gt;
|Email=filiale.duesseldorf@conrad.de  &lt;br /&gt;
|Weblink=http://www.conrad.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Essen===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Altendorfer Str. 11&lt;br /&gt;
|PLZ=45127&lt;br /&gt;
|Ort=Essen&lt;br /&gt;
|Telefon=01805-564445 &lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo. – Fr.    10.00 – 19.30 Uhr&amp;lt;br&amp;gt;Sa.  10.00 – 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.de&lt;br /&gt;
|Email=filiale.essen@conrad.de&lt;br /&gt;
|Bemerkung=schräg gegenüber von IKEA, Tiefgarage im UG für Kunden kostenlos, Karte an der Kasse lochen lassen&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Herne===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Electronic Hanke&lt;br /&gt;
|Straße=Wilhelmstr. 38 &lt;br /&gt;
|PLZ=44649&lt;br /&gt;
|Ort=Herne&lt;br /&gt;
|Telefon=02325-52728  &lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo. – Fr.    10.00 – 18.00 Uhr, außer Dienstags ab 15.00Uhr&amp;lt;br&amp;gt;Mittagspause von 13.00 Uhr - 15.00 Uhr&amp;lt;br&amp;gt;Sa.  9.30 – 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.electronic-hanke.de/&lt;br /&gt;
|Email=electronic_hanke@t-online.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Iserlohn===&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Techno-Center-Schmitt&lt;br /&gt;
|Straße=Raiffeisenstr. 14&lt;br /&gt;
|PLZ=58638&lt;br /&gt;
|Ort=Iserlohn&lt;br /&gt;
|Telefon=02371/2197931&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.–Fr. von 10:00 – 18:00 Uhr&amp;lt;br&amp;gt;Sa. von 10:00 – 14:00 Uhr &lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Köln===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=M. + M. van der Meyden GmbH&lt;br /&gt;
|Straße=Breite Straße 101&lt;br /&gt;
|PLZ=50667&lt;br /&gt;
|Ort=Köln&lt;br /&gt;
|Telefon=0221/2576369&lt;br /&gt;
|Fax=0221/2576369&lt;br /&gt;
|Öffnungszeiten=Mo.–Fr. von 9:30 – 19:00 Uhr&amp;lt;br&amp;gt;Sa. von 10:00 – 16:30 Uhr &lt;br /&gt;
|Weblink=http://vandermeyden.de&lt;br /&gt;
|Email=http://vandermeyden.de/?page_id=13&lt;br /&gt;
|Bemerkung=Sehr teuer (Standard Quarz HC49/S --&amp;gt; 1,50€); Muss manche µCs extra bestellen, die gängigsten AVR-Typen in DIP jedoch auf Lager&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Bergisch Gladbach===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=MS-Elektronik&lt;br /&gt;
|Straße=Laurentiusstraße 20&lt;br /&gt;
|PLZ=51465&lt;br /&gt;
|Ort=Bergisch Gladbach&lt;br /&gt;
|Telefon=02202 - 93 22 17&lt;br /&gt;
|Fax=02202 - 93 22 18&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr.: 09:00-12:30 &amp;amp; 14:30 - 18:30 Uhr&amp;lt;br&amp;gt;Sa: 09:00 - 14:00 Uhr&lt;br /&gt;
|Weblink=http://www.ms-elektronik.info/&lt;br /&gt;
|Email=info@ms-elektronik.info&lt;br /&gt;
|Bemerkung= Auch Versand&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=DARISUS GmbH&lt;br /&gt;
|Straße=Friedrich-Ebert-Str. 75 Haus 34/I&lt;br /&gt;
|PLZ=51429&lt;br /&gt;
|Ort=Bergisch Gladbach&lt;br /&gt;
|Telefon=02204 - 98 18 11&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=http://darisusgmbh.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=Nur Abholung von vorher online bestellten Teilen&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Langenfeld===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=rs elektronik - Reinhard Sinzel&lt;br /&gt;
|Straße=Solinger Straße 152&lt;br /&gt;
|PLZ=40764&lt;br /&gt;
|Ort=Langenfeld&lt;br /&gt;
|Telefon=02173/22766&lt;br /&gt;
|Fax=02173/25958&lt;br /&gt;
|Öffnungszeiten=Mo-Fr 10:00-13:00 + 15:00-18:00, Sa 10:00-13:00&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=zwischen Polizeiwache und Bahnunterführung auf der linken Seite. Nur Ladenverkauf!&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Moers===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Nürnberg Electronic&lt;br /&gt;
|Straße=Uerdinger Str. 121&lt;br /&gt;
|PLZ=47441&lt;br /&gt;
|Ort=Moers&lt;br /&gt;
|Telefon=02841-32221 &lt;br /&gt;
|Fax=02841-31733&lt;br /&gt;
|Öffnungszeiten=Mo.- Fr. 9:00 - 13:00 und 14:00 - 18:00&amp;lt;br&amp;gt;Samstag 9:00 - 13:00&amp;lt;br&amp;gt;Mittwoch Nachmittag geschlossen!&lt;br /&gt;
|Weblink=http://www.nuernberg-electronic.de/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Mönchengladbach===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Brunenberg Elektronik&lt;br /&gt;
|Straße=Lürriper Straße 170&lt;br /&gt;
|PLZ=41065&lt;br /&gt;
|Ort=Mönchengladbach&lt;br /&gt;
|Telefon=02161-44421&lt;br /&gt;
|Fax=02161-42552&lt;br /&gt;
|Öffnungszeiten=Mo.- Fr. 9:00 - 13:00 und 14:00 - 18:00&amp;lt;br&amp;gt;Samstag 9:00 - 13:00&amp;lt;br&amp;gt;Donnerstag Nachmittag geschlossen!&lt;br /&gt;
|Weblink=http://www.bruntronic.de&lt;br /&gt;
|Email=info@bruntronic.de&lt;br /&gt;
|Bemerkung=Das Ladengeschäft wurde geschlossen!&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Zilles Elektronik GmbH&lt;br /&gt;
|Straße=Aachener Str. 415&lt;br /&gt;
|PLZ=41069&lt;br /&gt;
|Ort=Mönchengladbach&lt;br /&gt;
|Telefon=02161/176005&lt;br /&gt;
|Fax=02161/176007&lt;br /&gt;
|Öffnungszeiten=Montag - Freitag 8:45 - 13:00 &amp;amp; 14:00 - 18:00&amp;lt;br&amp;gt;Samstag 9:30 - 13:00&lt;br /&gt;
|Weblink=http://www.zilles-elektronik.de/&lt;br /&gt;
|Email=info@zilles-elektronik.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Monheim am Rhein===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Schukat electronic Vertriebs GmbH&lt;br /&gt;
|Straße=Daimlerstraße 26&lt;br /&gt;
|PLZ=40789&lt;br /&gt;
|Ort=Monheim am Rhein&lt;br /&gt;
|Telefon=02173 - 950-5&lt;br /&gt;
|Fax=02173 - 950-999&lt;br /&gt;
|Öffnungszeiten=montags bis freitags zwischen 8 Uhr und 18 Uhr&lt;br /&gt;
|Weblink=http://www.schukat.com&lt;br /&gt;
|Email=info@schukat.com&lt;br /&gt;
|Internet: www.schukat.com&lt;br /&gt;
|Bemerkung=nur Gewerblich Teile können nach Vorbestellung auch abgeholt werden ! &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Münster===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Omega electronic GmbH &lt;br /&gt;
|Straße=Salzstraße 35&lt;br /&gt;
|PLZ=48143&lt;br /&gt;
|Ort=Münster&lt;br /&gt;
|Telefon=Tel:0251-3844540&lt;br /&gt;
|Fax=-&lt;br /&gt;
|Öffnungszeiten=Mo. – Sa.    10.00 – 19.00 Uhr&lt;br /&gt;
|Weblink=http://www.omega.ms/&lt;br /&gt;
|Email=info@omega.ms&lt;br /&gt;
|Bemerkung=Neu in Münster und sehr nah am Hauptbahnhof.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Oberhausen ===&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Elektro Urban&lt;br /&gt;
|Straße=Siegesstrasse 129a &lt;br /&gt;
|PLZ=46147&lt;br /&gt;
|Ort=Oberhausen&lt;br /&gt;
|Telefon=0208-94145419&lt;br /&gt;
|Fax=0208-94145418&lt;br /&gt;
|Öffnungszeiten=Mo. – Fr.    10.00 – 16.00 Uhr, Samstags geschlossen.&lt;br /&gt;
|Email=&lt;br /&gt;
|Weblink=http://www.elur.de/&lt;br /&gt;
|Bemerkung= Hinten im Hof! Ehemals Duisburg-Marxloh, Kaiser-Friedrich Str. 127&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Paderborn===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Jansen-Elektronik GmbH &amp;amp; Co. KG (GK-Elektronik Guido Kloss)&lt;br /&gt;
|Straße=Heiersstraße 24&lt;br /&gt;
|PLZ=33098 &lt;br /&gt;
|Ort=Paderborn&lt;br /&gt;
|Telefon=05251-282848&lt;br /&gt;
|Fax=05251-282851&lt;br /&gt;
|Öffnungszeiten=Mo. – Fr.    9.30 – 13.00 Uhr&amp;lt;br&amp;gt;Mo. – Fr.   14.30 – 18.30 Uhr&amp;lt;br&amp;gt;Sa.   9.30 – 14.00 Uhr&lt;br /&gt;
|Weblink=http://www.jansen-elektronik.de/&lt;br /&gt;
|Email=info@jansen-elektronik.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Recklinghausen===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Elektronik-Center Wenzlik, Inh. H.- J. Juhnke&lt;br /&gt;
|Straße=Halterner Straße 24&lt;br /&gt;
|PLZ=45657 &lt;br /&gt;
|Ort=Recklinghausen&lt;br /&gt;
|Telefon=02361-14103&lt;br /&gt;
|Fax=02361-182489&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr.: 9.00h-13.00h u. 14.30h-18.30h, Sa.: 10.00h-13.00h geöffnet!&lt;br /&gt;
|Weblink=http://www.ju-tec.de&lt;br /&gt;
|Email=ecw-recklinghausen@t-online.de&lt;br /&gt;
|Bemerkung= Elektronik Einzelhandel 35.000 verschiedene Artikel vorrätig.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Siegen===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Radig Hard &amp;amp; Software, Inh. U. Radig&lt;br /&gt;
|Straße=An der Bahn 18&lt;br /&gt;
|PLZ=57223&lt;br /&gt;
|Ort=Kreuztal&lt;br /&gt;
|Telefon=02732-762442&lt;br /&gt;
|Fax=02732-762443&lt;br /&gt;
|Öffnungszeiten=Nach Vereinbahrung&lt;br /&gt;
|Weblink=http://www.ulrichradig.de&lt;br /&gt;
|Email=mail@ulrichradig.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Wuppertal===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=WE elektronik&lt;br /&gt;
|Straße=Sedanstraße 88&lt;br /&gt;
|PLZ=42281&lt;br /&gt;
|Ort=Wuppertal&lt;br /&gt;
|Telefon=0202-510444&lt;br /&gt;
|Fax=0202-510666&lt;br /&gt;
|Öffnungszeiten=Mo.- Fr. 9.00 - 18.00&lt;br /&gt;
|Weblink=http://www.we-wuppertal.de/&lt;br /&gt;
|Email=info@we-wuppertal.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=K&amp;amp;K Elektronic&lt;br /&gt;
|Straße=Höhne 33&lt;br /&gt;
|PLZ=42275&lt;br /&gt;
|Ort=Wuppertal&lt;br /&gt;
|Telefon=0202&lt;br /&gt;
|Fax=0202&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Rheinland-Pfalz==&lt;br /&gt;
===Andernach===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=EDV + Elektronic Systeme Manuel Zitzer e.K.&lt;br /&gt;
|Straße=Füllscheuer 30&lt;br /&gt;
|PLZ=56626&lt;br /&gt;
|Ort=Andernach&lt;br /&gt;
|Telefon=02632/9293-0&lt;br /&gt;
|Fax=02632/9293-33&lt;br /&gt;
|Öffnungszeiten=Montag-Freitag 8:00 Uhr - 12:00 Uhr und 14:00 Uhr - 18:00 Uhr, Dienstag zusätzlich bis 19:00 Uhr, Samstag geschlossen&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
|Weblink=http://www.eleksys.de/&lt;br /&gt;
|Email=info@eleksys.de&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Kaiserslautern===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=BURCKHARDT-ELEKTRONIK&lt;br /&gt;
|Straße=Waldstr. 17&lt;br /&gt;
|PLZ=67659 &lt;br /&gt;
|Ort=Kaiserslautern&lt;br /&gt;
|Telefon=+49 (0)631 70114&lt;br /&gt;
|Fax=49 (0)631 70162&lt;br /&gt;
|Öffnungszeiten=Montag-Donnerstag 8:00 Uhr - 16:45 Uhr/Freitag 8:00 Uhr - 12:00 Uhr&lt;br /&gt;
|Weblink=http://www.burckhardt-elektronik.de&lt;br /&gt;
|Email=burckhardt-elektronik@web.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Koblenz===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Radio Erbar&lt;br /&gt;
|Straße=Bahnhofstr. 40&lt;br /&gt;
|PLZ=56068  &lt;br /&gt;
|Ort=Koblenz&lt;br /&gt;
|Telefon=0261/34782&lt;br /&gt;
|Fax=0261/14570&lt;br /&gt;
|Öffnungszeiten=Montag-Freitag 9:00 Uhr - 18:00 Uhr/Samstag 9:00 Uhr - 12:00 Uhr&lt;br /&gt;
|Weblink=http://www.radio-erbar.de/&lt;br /&gt;
|Email=webmaster@radio-erbar.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
===Mainz===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Binger Str. 14-16 (nähe Hauptbahnhof)&lt;br /&gt;
|PLZ=55122 &lt;br /&gt;
|Ort=Mainz&lt;br /&gt;
|Telefon=0180 5312111&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=http://www.conrad.de/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Elektronik Schmidt&lt;br /&gt;
|Straße=Boppstraße 62 - 64&lt;br /&gt;
|PLZ=55118 &lt;br /&gt;
|Ort=Mainz&lt;br /&gt;
|Telefon=0180 5312111&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Montag - Freitag 09.00 Uhr - 13.00 Uhr und 14.00 Uhr - 18.00 Uhr&amp;lt;br&amp;gt;&lt;br /&gt;
Samstag 09.00 Uhr - 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.schmidt-electronic.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Saarland==&lt;br /&gt;
=== Homburg ===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Werner Fauss Elektronik&lt;br /&gt;
|Straße=Am Schützenhof 1&lt;br /&gt;
|PLZ=66424&lt;br /&gt;
|Ort=Homburg&lt;br /&gt;
|Telefon=+49 (6841) 74988&lt;br /&gt;
|Fax= +49 (6841) 74980&lt;br /&gt;
|Öffnungszeiten= Mo-Fr 8:00-12:00 Uhr / 14:00-17:00 Uhr&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=wf-elektronik@gmx.de&lt;br /&gt;
|Bemerkung=Grosshandel mit elektronischen Bauteilen und Komponenten auch Privatverkauf.&lt;br /&gt;
}}&lt;br /&gt;
=== Saarbrücken ===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=ESS Elektronik Service Skowronek&lt;br /&gt;
|Straße=Peter-Zimmer-Str. 13&lt;br /&gt;
|PLZ=66123  &lt;br /&gt;
|Ort=Saarbrücken&lt;br /&gt;
|Telefon=+49 (681) 816414&lt;br /&gt;
|Fax= +49 (681) 816992&lt;br /&gt;
|Öffnungszeiten= Mo-Fr 8:00-12:00 Uhr / 14:00-18:00 Uhr&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad&lt;br /&gt;
|Straße=Trierer Straße 16-20&lt;br /&gt;
|PLZ=66111&lt;br /&gt;
|Ort=Saarbrücken&lt;br /&gt;
|Telefon=0180 5 564445 (derzeit 14 Cent/Min. aus dem Festnetz der Dt. Telekom. Evtl. abweichende Preise für Anrufe aus den Mobilfunknetzen.)&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten= Mo.-Fr. 09.00-19.00 Uhr / Sa. 09.00-19.00 Uhr&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=EXP Tech GmbH&lt;br /&gt;
|Straße=Meerwiesertalweg 23&lt;br /&gt;
|PLZ=66123  &lt;br /&gt;
|Ort=Saarbrücken&lt;br /&gt;
|Telefon=+49 (681) 95819340 &lt;br /&gt;
|Fax= +49 (681) 954176-19 &lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink= http://www.exp-tech.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= Nur online Verkauf &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Sachsen==&lt;br /&gt;
===Chemnitz===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=mükra electronic shop GmbH&lt;br /&gt;
|Straße=Reichsstraße 58&lt;br /&gt;
|PLZ=09113&lt;br /&gt;
|Ort=Chemnitz&lt;br /&gt;
|Telefon=0371/365736&lt;br /&gt;
|Fax=0371/365736&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 10.00 Uhr - 18.00 Uhr&amp;lt;br&amp;gt;Sa. 10.00 Uhr - 13.00 Uhr&lt;br /&gt;
|Weblink=http://www.muekra.com/filiale_chemnitz.html&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=gut sortiert aber keine SMD BE | wirkt manchmal recht unfreundlich und demotiviert&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=köhler-elektronik&amp;lt;br&amp;gt;Firma Michael Köhler&lt;br /&gt;
|Straße=Erfenschlager Straße 31&lt;br /&gt;
|PLZ=09125&lt;br /&gt;
|Ort=Chemnitz&lt;br /&gt;
|Telefon=(03 71) 51 91 03&lt;br /&gt;
|Fax=(03 71) 51 91 04&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. von 9 - 18 Uhr&lt;br /&gt;
|Weblink=http://www.koehler-elektronik.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Dresden===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad&lt;br /&gt;
|Straße=Friedrich-List-Platz 2&amp;lt;br&amp;gt;gegenüber Hauptbahnhof&lt;br /&gt;
|PLZ=01069&lt;br /&gt;
|Ort=Dresden &lt;br /&gt;
|Telefon=&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 09.30-20.00 Uhr, &amp;lt;br&amp;gt;Sa. 09.00-20.00 Uhr&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= verhältnismäßig teuer&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Sullus&lt;br /&gt;
|Straße=Tharandter Str. 67&lt;br /&gt;
|PLZ=01187&lt;br /&gt;
|Ort=Dresden &lt;br /&gt;
|Telefon=0351 4112100&lt;br /&gt;
|Fax=0351 4112146&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 10.00-18.30 Uhr, &amp;lt;br&amp;gt;Sa. 09.00-12.00 Uhr&lt;br /&gt;
|Weblink=http://www.sullus.de/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Elektronic-Shop Meissen&amp;lt;br&amp;gt;Rainer Pötzsch&lt;br /&gt;
|Straße=Neugasse 34&lt;br /&gt;
|PLZ=01662&lt;br /&gt;
|Ort=Meissen&lt;br /&gt;
|Telefon= +49 3521 452301&lt;br /&gt;
|Fax= +49 3521 452399&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. von 9 - 19 Uhr &amp;lt;br&amp;gt; Sa. von 9 - 32 Uhr&lt;br /&gt;
|Weblink=http://www.electronic-poetzsch.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Leipzig===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad&lt;br /&gt;
|Straße=Neumarkt 20&lt;br /&gt;
|PLZ=04109&lt;br /&gt;
|Ort=Leipzig &lt;br /&gt;
|Telefon=&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Sa.: 09.30-20.00 Uhr&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=ELMICRO Computer GmbH &amp;amp; Co. KG &lt;br /&gt;
|Straße=Hohe Str. 9-13&lt;br /&gt;
|PLZ=04107&lt;br /&gt;
|Ort=Leipzig &lt;br /&gt;
|Telefon=+49-(0)341-9104810&lt;br /&gt;
|Fax=+49-(0)341-9104818&lt;br /&gt;
|Öffnungszeiten=Mo.-Sa.: 09.00-17.00 Uhr&lt;br /&gt;
|Weblink=http://elmicro.com/de/ela-leipzig.html&lt;br /&gt;
|Email=leipzig|at|elmicro.com&lt;br /&gt;
|Bemerkung=Besucher werden gebeten, sich kurzfristig telefonisch anzumelden.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Zwickau===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Elektronik Neumerkel GmbH&lt;br /&gt;
|Straße=Kolpingstraße 20&lt;br /&gt;
|PLZ=08058&lt;br /&gt;
|Ort=Zwickau&lt;br /&gt;
|Telefon=+ 49 (0)375-589920&lt;br /&gt;
|Fax=+ 49 (0)375-5899222&lt;br /&gt;
|Öffnungszeiten=Mo. - Fr. 9:00 - 18:00 Uhr, Sa. 9:00 - 12:30 Uhr&lt;br /&gt;
|Weblink=http://www.neumerkel.de&lt;br /&gt;
|Email=info@neumerkel.de&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Sachsen-Anhalt==&lt;br /&gt;
===Magdeburg===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=mittrenga electronic&lt;br /&gt;
|Straße=Maxim-Gorki-Str. 34&lt;br /&gt;
|PLZ=39108&lt;br /&gt;
|Ort=Magdeburg&lt;br /&gt;
|Telefon=0391/7333500&lt;br /&gt;
|Fax=0391/7346538&lt;br /&gt;
|Öffnungszeiten= 15.00- 18.00&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=Keine Microcontroller&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Halle (Saale)===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Funkhaus Alter Markt&lt;br /&gt;
|Straße=Alter Markt 6&lt;br /&gt;
|PLZ=06108&lt;br /&gt;
|Ort=Halle&lt;br /&gt;
|Telefon=0345/2831651&lt;br /&gt;
|Fax=0345/2831651&lt;br /&gt;
|Öffnungszeiten=Mo bis Fr&lt;br /&gt;
|Weblink=http://www.fernsehklinik-halle.de/&lt;br /&gt;
|Email=Service@Fernsehklinik-Halle.de&lt;br /&gt;
|Bemerkung=elektronische Bauteile aller Art gut sortiert auf Lager. Nach Möglichkeit vorher telefonisch nachfragen(!), damit das Teil aus dem Lager geholt werden kann, insgesamt vergleichsweise teuer, ansonsten recht unkompliziert&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Westfalia&lt;br /&gt;
|Straße=Grenzstraße 35&lt;br /&gt;
|PLZ=06112&lt;br /&gt;
|Ort=Halle&lt;br /&gt;
|Telefon=0345 560 62 31&lt;br /&gt;
|Fax=0345 560 62 32 &lt;br /&gt;
|Öffnungszeiten= &lt;br /&gt;
Montag - Mittwoch 08.00 - 18.30&lt;br /&gt;
&lt;br /&gt;
Donnerstag        08.00 - 19.00&lt;br /&gt;
&lt;br /&gt;
Freitag           08.00 - 18.30&lt;br /&gt;
&lt;br /&gt;
Samstag           08.30 - 13.30 &lt;br /&gt;
&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=Elektronikmäßig relativ klein sortiert, keine einzelnen Bauelemente, dafür Restposten-/Sortimentebeutel mit jeweils verschiedenen Widerständen, Kondensatoren, Transistoren, LEDs etc. &lt;br /&gt;
Ferner Platinen, vereinzelt Trafos, jede Menge Bausätze, Lötzubehör, Lautsprecher, Stecker, Buchsen und Kabel aller Art, Gehäuse&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Schleswig Holstein==&lt;br /&gt;
===Kiel===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Elektronik Schmidt Inh. Karl Heinz Parting&lt;br /&gt;
|Straße=Adelheidstr. 28&lt;br /&gt;
|PLZ=24103&lt;br /&gt;
|Ort=Kiel&lt;br /&gt;
|Telefon=0431 94682&lt;br /&gt;
|Fax=0431 92574&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= viele historische Bauteile verfügbar, Röhren&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Lübeck===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Lenzner Jürgen Elektronik GmbH&lt;br /&gt;
|Straße=Krähenstraße 19&lt;br /&gt;
|PLZ=23552&lt;br /&gt;
|Ort=Lübeck&lt;br /&gt;
|Telefon=0451 77336&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= keine uC, teils historische Bauteile verfügbar, Röhren, LEDs überteuert&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Schwentinental===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Mergenthalerstr. 22&lt;br /&gt;
|PLZ=24223&lt;br /&gt;
|Ort=Schwentinental, OT Raisdorf&lt;br /&gt;
|Telefon=0180 5 564445 &lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Sa. 10.00-20.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.de&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Thüringen==&lt;br /&gt;
===Eisenach===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Jena===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Stefan Schmutzer VAT Elektronik&lt;br /&gt;
|Straße=Bachstraße 10&lt;br /&gt;
|PLZ=07743&lt;br /&gt;
|Ort=Jena&lt;br /&gt;
|Telefon= (03641)447184&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten= Mo-Fr. bis 18:00Uhr. Sa zu&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= reichlich teuer. Für einzelne Kleinteile jedoch definitiv zu empfehlen, wenn man nicht gleich bestellen will&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===Gera===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname= Neumerkel Elektronik&lt;br /&gt;
|Straße=Karl-Schurz-Straße 12&lt;br /&gt;
|PLZ=07545&lt;br /&gt;
|Ort=Gera&lt;br /&gt;
|Telefon= +49 (0) 3 65 - 82 46 90&lt;br /&gt;
|Fax= 	+49 (0) 3 65 - 82 46 922&lt;br /&gt;
|Öffnungszeiten= Mo-Fr. 9.00 bis 18:00Uhr. Sa 9.00 bis 12.00 &lt;br /&gt;
|Weblink= http://www.neumerkel.de/gera.htm&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=Österreich=&lt;br /&gt;
==Linz==&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Aigner Elektronik&lt;br /&gt;
|Straße=Dinghoferstr. 63&lt;br /&gt;
|PLZ=A-4020 &lt;br /&gt;
|Ort=Linz&lt;br /&gt;
|Telefon=+43 732 669691-0 &lt;br /&gt;
|Fax=+43 732 669691-15&lt;br /&gt;
|Öffnungszeiten=Mo. bis Fr. 8:30 bis 17:00 (keine Mittagssperre), Samstag 8:30 bis 12:00&lt;br /&gt;
|Weblink=http://www.aigner.co.at/&lt;br /&gt;
|Email= &lt;br /&gt;
|Bemerkung= September 2014: Geschäft aufgelassen !!&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic&lt;br /&gt;
|Straße=Kornstraße 4&lt;br /&gt;
|PLZ=A-4060 &lt;br /&gt;
|Ort=Leonding&lt;br /&gt;
|Telefon=+43 732 683040-0  &lt;br /&gt;
|Fax=+43 732 683040-13 &lt;br /&gt;
|Öffnungszeiten=Mo. bis Fr. 9:00 bis 19:00 , Samstag 9:00 bis 17:00&lt;br /&gt;
|Weblink=http://www.conrad.at/&lt;br /&gt;
|Email=mailto://filiale.linz@conrad.at&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Graz== &lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Neuhold Elektronik&lt;br /&gt;
|Straße=Griesplatz 1&lt;br /&gt;
|PLZ=A-8020 &lt;br /&gt;
|Ort=Graz&lt;br /&gt;
|Telefon=+43 (0) 316 711245   &lt;br /&gt;
|Fax=+43 (0) 316 717419&lt;br /&gt;
|Öffnungszeiten=Montag bis Freitag von 9.00 - 18.00 Uhr&amp;lt;br&amp;gt;Samstag 9.00-12.30 Uhr&lt;br /&gt;
|Weblink=http://www.neuhold-elektronik.at/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=Großes Sortiment mit auch sehr ausgefallenen Artikeln. &amp;lt;br&amp;gt; Führt eine breite Produktpalette. &amp;lt;br&amp;gt; Durchweg sehr günstige Preise, jedoch manchmal bei Standardbauteilen (Mikrocontrollern z.B. AVRs) teurer als die Konkurrenz. &amp;lt;br&amp;gt; Vergleichbar mit Pollin.  &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=L-Tronik Austria&lt;br /&gt;
|Straße=Karlauerstraße 5&lt;br /&gt;
|PLZ=A-8020 &lt;br /&gt;
|Ort=Graz&lt;br /&gt;
|Telefon=Tel: +43 (0) 316 904 672            &lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Montag bis Freitag von 9.00 - 17.00 Uhr &amp;lt;br&amp;gt;Samstag 9.00-12.00 Uhr&lt;br /&gt;
|Weblink=http://www.l-tronik.com/index.php&lt;br /&gt;
|Email=info@lta.at&lt;br /&gt;
|Bemerkung=Riesiges Sortiment an Halbleitern (Auch SMD). Solartechnik, Sicherheitstechnik, Haustechnik u.s.w..&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad&lt;br /&gt;
|Straße=Weblinger Gürtel 25&lt;br /&gt;
|PLZ=A-8054&lt;br /&gt;
|Ort=Graz&lt;br /&gt;
|Telefon=Tel: +43 (0) 50 - 20 40 73 00         &lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Montag bis Freitag von 9.00 - 19.30 Uhr &amp;lt;br&amp;gt;Samstag 9.00 - 18.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.at&lt;br /&gt;
|Email=filiale.graz@conrad.at&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Salzburg==&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad&lt;br /&gt;
|Straße=Alpenstraße 95 - 97&lt;br /&gt;
|PLZ=5020  &lt;br /&gt;
|Ort=Salzburg&lt;br /&gt;
|Telefon=050 - 20 40 81 00&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9.00-19.00 Uhr&amp;lt;br&amp;gt;Sa. 9.00-18.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.at/megastores&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
==Wien==&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Technotronic&lt;br /&gt;
|Straße=ZIEGLERGASSE 27&lt;br /&gt;
|PLZ=1070&lt;br /&gt;
|Ort=Wien&lt;br /&gt;
|Telefon=+43 1 5236204&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Do. 9.00-18.00 Uhr&amp;lt;br/&amp;gt;Fr. 9-15&lt;br /&gt;
|Weblink=&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=auch andere Filialen in Wien&amp;lt;br/&amp;gt;05.08.2013 Zettel mit &amp;quot;aufgelassen&amp;quot; im Fenster&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad&lt;br /&gt;
|Straße=Hütteldorfer Straße 81b (Meiselmarkt U3 Johnstrasse)&lt;br /&gt;
|PLZ=1150 &lt;br /&gt;
|Ort=Wien&lt;br /&gt;
|Telefon=050 - 20 40 75 00&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9.00-19.00 Uhr&amp;lt;br/&amp;gt;Sa. 9.00-18.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.at/megastores&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad&lt;br /&gt;
|Straße=Gewerbeparkstraße 12 (Gewerbepark Stadlau)&lt;br /&gt;
|PLZ=1220 &lt;br /&gt;
|Ort=Wien&lt;br /&gt;
|Telefon=050 - 20 40 72 00&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9.00-19.00 Uhr&amp;lt;br/&amp;gt;Sa. 9.00-18.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.at/megastores&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad&lt;br /&gt;
|Straße=Nordring 2&lt;br /&gt;
|PLZ=2334 &lt;br /&gt;
|Ort=Vösendorf/Süd&lt;br /&gt;
|Telefon=050 - 20 40 71 00&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=Mo.-Fr. 9.00-19.00 Uhr&amp;lt;br/&amp;gt;Mi 9.00-20.00 Uhr&amp;lt;br/&amp;gt;Sa. 9.00-18.00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.at/megastores&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=Schweiz=&lt;br /&gt;
----&lt;br /&gt;
== Basel-Landschaft (BL) ==&lt;br /&gt;
=== 4450 Sissach ===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Grieder Elektronik Bauteile AG&lt;br /&gt;
|Straße=Reuslistrasse 62&lt;br /&gt;
|PLZ=4450&lt;br /&gt;
|Ort=Sissach&lt;br /&gt;
|Telefon=061 976 95 95&lt;br /&gt;
|Fax=061 976 95 90&lt;br /&gt;
|Öffnungszeiten=Mo-Do 800-1200, 1300-1600 und Fr 800-1200, 1300-1500.&lt;br /&gt;
|Weblink=http://shop.griederbauteile.ch/&lt;br /&gt;
|Email=info@griederbauteile.ch&lt;br /&gt;
|Bemerkung=Mindestbestellwert CHF 20. Vorbestellte Waren können abgeholt werden.  Der &amp;quot;Laden&amp;quot; befindet sich 10 Minuten zu Fuss vom Bahnhof Sissach.&lt;br /&gt;
&lt;br /&gt;
}}&lt;br /&gt;
== Bern (BE) ==&lt;br /&gt;
=== 2560 Nidau ===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Redacom AG / thinkembedded.ch Web Shop&lt;br /&gt;
|Straße=Hauptstrasse 96&lt;br /&gt;
|PLZ=2560&lt;br /&gt;
|Ort=Nidau&lt;br /&gt;
|Telefon=+41 32 332 99 55&lt;br /&gt;
|Fax=+41 32 332 99 59&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
|Weblink =http://redacom.ch/ Shop: http://thinkembedded.ch/ &lt;br /&gt;
|Email=order@redacom.ch&lt;br /&gt;
|Bemerkung=Bestellungen im Onlineshop können wahlweise versendet oder abgeholt werden. &lt;br /&gt;
&#039;&#039;&#039;Sortiment:&#039;&#039;&#039;&amp;lt;br&amp;gt;Diverse Microcontroller Boards, Debugger, Programmer und USB-Messinstrumente&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Luzern (LU) ==&lt;br /&gt;
=== Emmenbrücke ===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic Schweiz&lt;br /&gt;
|Straße=Seetalstrasse 11&lt;br /&gt;
|PLZ=6020&lt;br /&gt;
|Ort=Emmenbrücke &lt;br /&gt;
|Telefon=0848/80 12 83&lt;br /&gt;
|Fax=041/267 32 14&lt;br /&gt;
|Öffnungszeiten=Mo/Di/Do 09:00-18:30, Mi/Fr 09:00-21:00, Sa 08:00-16:00 Uhr&lt;br /&gt;
|Weblink=http://www1.ch2.conrad.com/infocenter/filialen.php&lt;br /&gt;
|Email=filiale.emmenbruecke@conrad.ch&lt;br /&gt;
|Bemerkung=5 Minuten vom Bahnhof Emmenbrücke&amp;lt;br&amp;gt;GRATIS Parkhaus &amp;amp; Parkplätze &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Solothurn (SO) ==&lt;br /&gt;
===5014 Gretzenbach===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=EFG Electronic AG&lt;br /&gt;
|Straße=Köllikerstrasse 32&lt;br /&gt;
|PLZ=5014&lt;br /&gt;
|Ort=Gretzenbach&lt;br /&gt;
|Telefon= 062 849 23 61 &lt;br /&gt;
|Fax= 062 849 23 70 &lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
Mo-Fr 08:30-11:30, 13:30-18:30, Mi geschlossen, Sa 09:00-16:00 &lt;br /&gt;
|Weblink=http://www.maxland.ch/netmax/standseiten/efg/index.html&lt;br /&gt;
|Email=efgag@yetnet.ch &lt;br /&gt;
|Bemerkung=Kabel - Messgeräte - Lautsprecher - elektronische Bauteile&lt;br /&gt;
kein Versand, nur Ladengeschäft&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Zürich (ZH) ==&lt;br /&gt;
===8004 Zürich===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Pusterla Elektronik AG&lt;br /&gt;
|Straße=Hohlstrasse 52 &lt;br /&gt;
|PLZ=8004&lt;br /&gt;
|Ort= Zürich&lt;br /&gt;
|Telefon=044 241 56 77&lt;br /&gt;
|Fax=044 242 01 04&lt;br /&gt;
|Öffnungszeiten=&lt;br /&gt;
Mo-Fr 09:00-18:30, Sa 09:00-16:00&lt;br /&gt;
|Weblink=http://www.pusterla.ch/&lt;br /&gt;
|Email=info@pusterla.ch&lt;br /&gt;
|Bemerkung=Absoluter &amp;quot;Kult-Laden&amp;quot; mit Tradition. Wer jemals in der Schweiz einen Lötkolben in der Hand hatte, der kennt &amp;quot;Pusti&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Das Sortiment ist zweigeteilt:&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;hinter dem Tresen:&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
gutes allgemeines Bauteilesortiment, spezielle Sachen werden bestellt.&lt;br /&gt;
&amp;lt;br&amp;gt;Man nimmt sich Zeit für Fachberatung - es wird auch schon mal ein Vergleichstyp aus der Liste gesucht und dem jungen &amp;quot;Case-Modder&amp;quot; wird mit Engelsgeduld erklärt wie man den Vorwiderstand für seine coole LED-Prozessorinnenbeleuchtung berechnet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;vor dem Tresen - Selbstbedienung:&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Am Eingang nimmt man sich eine Pappschale mit Bleistift und Notizblock.&lt;br /&gt;
Artikel, Menge und Preise schreibt man selbst auf!&lt;br /&gt;
&lt;br /&gt;
Das Sortiment bietet einen Querschnitt durch die elektronische Bauteilefertigung der letzten 50 Jahre. Sehr gute Quelle für spannungsfeste Kondensatoren und hochohmige (Leistungs-)Widerstände sowie mechanischem &amp;quot;Klein-Grabbel-Kram.&amp;quot; Gute Auswahl an Gehäusen, Transformatoren (z.T. recht schräge Typen) sowie Relais und Stecker/Buchsen die die Welt wohl niemals gebraucht hat. &amp;lt;br&amp;gt; &#039;&#039;&#039;Vorsicht bei Elektrolytkondensatoren:&#039;&#039;&#039; &amp;quot;historische&amp;quot; Lagerware, besser man hat ein ESR-Meter dabei - sodenn man hat!&lt;br /&gt;
&amp;lt;br&amp;gt;Kabel jeglicher Art gibt es ab der Rolle zum Selberabschneiden - auch 10cm sind kein Problem - ausrechnen und aufschreiben machst Du ja selber.&lt;br /&gt;
&amp;lt;br&amp;gt; Präsentation der Ware im Selbsbedienungsteil:&lt;br /&gt;
&amp;lt;br&amp;gt; Bauteile offen oder ab Gurt in kleinen, liebevoll angeschriebenen Pappschachteln (noch von Vater Pusterla), grösseren Wühlschachteln, einer Wühlecke sowie hier und da einige Merkwürdigkeiten auf dem Fussboden.&lt;br /&gt;
Man stelle sich das Ladenlokal eines Joint-Ventures aus Oppermann, Pollin, dem ehem. Völkner sowie dem örtlichen Entsorgungshof vor - &#039;&#039;&#039;das&#039;&#039;&#039; ist &amp;quot;Pusti&amp;quot; und so war er schon immer!&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; absolut lohnenswert, auch wenn man vielleicht nicht immer das bekommt was man wollte, dafür findet man aber alles, wonach man nie gesucht hatte!&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===8092 Zürich===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=&amp;quot;Bastli&amp;quot;&amp;lt;br&amp;gt;Fachverein der Departemente Informationstechnologie und Elektrotechnik und Maschinenbau und Verfahrenstechnik an der ETH Zürich.&lt;br /&gt;
|Straße=Universitätsstrasse 19&lt;br /&gt;
|PLZ=8092&lt;br /&gt;
|Ort= Zürich&lt;br /&gt;
|Telefon= n/a&lt;br /&gt;
|Fax= n/a&lt;br /&gt;
|Öffnungszeiten=Die Öffnungszeiten gelten nur während des Semesters.&lt;br /&gt;
Mo 12:15 - 13:00 Uhr&amp;lt;br&amp;gt;&lt;br /&gt;
Do 12:15 - 13:00 Uhr&amp;lt;br&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
|Weblink=http://www.bastli.ethz.ch/&lt;br /&gt;
|Email=bastli@amiv.ethz.ch&lt;br /&gt;
|Bemerkung=Studentischer &amp;quot;Bastel-Shop&amp;quot;&lt;br /&gt;
Während den Öffnungszeiten ist der Bastli-Shop im ersten Stock des UNG geöffnet.&lt;br /&gt;
Ihr könnt Bauteile, welche wir an Lager haben, kaufen und euch wird mit diversen elektronischen Problemen geholfen. &lt;br /&gt;
&lt;br /&gt;
Organisierte Sammelbestellungen bei Fa. Reichelt / Deutschland&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Standort:&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bastli und Messplatz befinden sich in den Räumlichkeiten des AMIV im UNG Gebäude schräg gegenüber des CAB.&lt;br /&gt;
Es ist das gleiche Gebäude in dem auch der ehem. AMIV-Verlag respektive ehem. WBS respektive SPOD untergebracht ist.&lt;br /&gt;
&lt;br /&gt;
Das Gebäude wirkt beim ersten Kontakt wohl für jeden neuen ein bisschen abschreckend. Aber keine Scheu, wenn man durch das etwas schlecht beleuchtete Treppenhaus in den ersten Stock gelangt, wendet man sich dort gleich nach rechts. Bastli und Messplatz haben die Zimmernummern C6 und C5.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== 8305 Dietlikon ===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Conrad Electronic Schweiz&lt;br /&gt;
|Straße=Alte Dübendorferstrasse 17&lt;br /&gt;
|PLZ=8305&lt;br /&gt;
|Ort=Dietlikon&lt;br /&gt;
|Telefon=0848/80 12 84 (Normaltarif)&lt;br /&gt;
|Fax=044/805 35 14&lt;br /&gt;
|Öffnungszeiten=Mo-Sa 09:00-20:00 Uhr&lt;br /&gt;
|Weblink=http://www.conrad.ch/ce/de/ChainstoreInfo.html?detail&amp;amp;chainstorecode=CS_CH_ZH&lt;br /&gt;
|Email=filiale.dietlikon@conrad.ch&lt;br /&gt;
|Bemerkung=5 Minuten vom Bahnhof Dietlikon&amp;lt;br&amp;gt;Parkhaus &amp;amp; Parkplätze vorhanden&lt;br /&gt;
(Stand: 2012-08-27)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== 8606 Nänikon ===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Distrelec Schweiz&lt;br /&gt;
|Straße=Grabenstrasse 6&lt;br /&gt;
|PLZ=8606&lt;br /&gt;
|Ort=Nänikon&lt;br /&gt;
|Telefon=044 - 944 99 11&lt;br /&gt;
|Fax=044 - 944 99 88&lt;br /&gt;
|Öffnungszeiten=Mo-Fr 07:30-18:00 Uhr (nur Abholschalter)&lt;br /&gt;
|Weblink=https://www.distrelec.ch&lt;br /&gt;
|Email=info@distrelec.com&lt;br /&gt;
|Bemerkung=&#039;&#039;&#039;Abholschalter:&#039;&#039;&#039; (Vorbestellung unbedingt erforderlich)&lt;br /&gt;
Telefonisch oder online bestellte Ware kann nach ca. 2 Stunden abgeholt werden.&lt;br /&gt;
&amp;lt;br&amp;gt;Es kann auch direkt vor Ort ab Katalog bestellt werden, allerdings dann Wartezeit von min. 2 Stunden&lt;br /&gt;
&amp;lt;br&amp;gt;Bezahlung: bar/EC- und Post-Card&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== St. Gallen (SG) ==&lt;br /&gt;
=== 9443 Widnau ===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Asif Elektronik&lt;br /&gt;
|Straße=Schützenstrasse 35&lt;br /&gt;
|PLZ=9443&lt;br /&gt;
|Ort=Widnau&lt;br /&gt;
|Telefon=071/722 01 57 (Normaltarif)&lt;br /&gt;
|Fax=071/588 02 58&lt;br /&gt;
|Öffnungszeiten=Mo-Fr 14:00-17:00 Uhr (Information und Support)&lt;br /&gt;
|Weblink=http://www.asif-elektronik.ch&lt;br /&gt;
|Email=sales@asif-elektronik.ch&lt;br /&gt;
|Bemerkung= Sprachen: Englisch und Deutsch.&lt;br /&gt;
Online Geschäft. Lieferung per Post. Abholung nur nach Vereinbarung.&lt;br /&gt;
&amp;lt;br&amp;gt;Bezahlung: Überweisung/Paypal/Kreditkarte&lt;br /&gt;
&amp;lt;br&amp;gt;Gratis Versand ab 50 CHF.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
= Luxembourg =&lt;br /&gt;
=== L-1510 Luxembourg ===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=Schallllller Electronic S.à.r.l&lt;br /&gt;
|Straße=19, Av. de la Faïencerie&lt;br /&gt;
|PLZ=1510&lt;br /&gt;
|Ort=Luxembourg&lt;br /&gt;
|Telefon=+352-475239-1&lt;br /&gt;
|Fax=+352-471507&lt;br /&gt;
|Öffnungszeiten=Montag - Freitag: 08:30 - 12:30, 13:30 - 18:00&amp;lt;br&amp;gt;Samstag: 09:00 - 13:00&lt;br /&gt;
|Weblink=http://schaller-electronic.lu/index.php?home&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== L-6905 Niederanven ===&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname=electronic-Shop S.àr.l. &lt;br /&gt;
|Straße=141, route de Trèves&lt;br /&gt;
|PLZ=6905&lt;br /&gt;
|Ort=Niederanven&lt;br /&gt;
|Telefon=+352 269464-1&lt;br /&gt;
|Fax=+352 269464-64&lt;br /&gt;
|Öffnungszeiten=&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|Weblink=www.electronic-shop.lu&lt;br /&gt;
|Email=info@electronic-shop.lu&lt;br /&gt;
|Bemerkung=Online-Bestellungen, die ein paar Tage später an der Abholtheke abgeholt werden können.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
= Türkei =&lt;br /&gt;
== Istanbul ==&lt;br /&gt;
=== Istanbul ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{ElektronikLieferant&lt;br /&gt;
|Firmenname= Ufuk Elektronik&lt;br /&gt;
|Straße=Yemişçi Hasan Sok. 1&lt;br /&gt;
|PLZ=&lt;br /&gt;
|Ort=&lt;br /&gt;
|Telefon=&lt;br /&gt;
|Fax=&lt;br /&gt;
|Öffnungszeiten=&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|Weblink=http://www.ufukelektronik.com/&lt;br /&gt;
|Email=&lt;br /&gt;
|Bemerkung= Im Gebiet des Fähranleger Karaköy gibt es einige Läden die Bauelemente anbieten. Der aufgeführte Laden wurde nur Beispielhaft aufgeführt. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
= Siehe auch =&lt;br /&gt;
* [[Platinenhersteller]]&lt;br /&gt;
* [[Elektronikversender]]&lt;br /&gt;
* [[Eisenwarenversender]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Lieferanten]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=K%C3%BChlk%C3%B6rper&amp;diff=48340</id>
		<title>Kühlkörper</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=K%C3%BChlk%C3%B6rper&amp;diff=48340"/>
		<updated>2010-06-08T12:11:37Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Einleitung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kühlkörper wird benötigt, wenn die Verlustleistung von elektronischen Bauteilen einen bestimmten Wert übersteigt. Der Kühlkörper hat dabei die Aufgabe, die entstehende Wärme möglichst gut auf eine große Fläche zu verteilen, um sie dann an die Umgebung abzugeben.&lt;br /&gt;
&lt;br /&gt;
[[bild: Rippenkuehlkoerper.png | thumb | 300px| Rippenkühlkörper, Schnittdarstellung]]&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip sieht man an jedem Rippenkühlkörper. An der Stelle, wo das Kühlobjekt angeschraubt/gepresst ist, ist ein dicker &amp;quot;Klumpen&amp;quot; Material, der erstmal die Wärme auf eine etwas größere Fläche verteilen soll. Bei CPU-Lüftern ist dort teilweise Kupfer eingepresst, weil das noch besser als Aluminium Wärme leitet (engl. heat spreader, Wärmespreizer). Danach folgen viele, nach aussen dünner werdende Rippen. In der Mitte noch dick, dort muss die Wärme ja verlustarm durchgeleitet werden, aussen dünn, dort ist fast alle Wärme schon abgegeben. Ein guter Kühlkörper hat eine möglichst große Oberfläche bei möglichst kleiner Masse. Viele Kühlkörper werden aus preiswerten [http://de.wikipedia.org/wiki/Strangpressen Strangpressprofilen] hergestellt.&lt;br /&gt;
&lt;br /&gt;
== Wärmewiderstand ==&lt;br /&gt;
&lt;br /&gt;
Die wichtigste Kennzahl eines Kühlkörpers ist der Wärmewiderstand. Er gibt an, um wie viel Kelvin sich die Temperatur zwischen Wärmequelle und Umgebung unterscheidet, wenn eine bestimmte Wärmeleistung abgeführt werden muss. Die Einheit ist K/W, Kelvin pro Watt. (Hinweis: Oft wird die Einheit °C/W angegeben, das ist allerdings nicht ganz korrekt. Temperaturdifferenzen werden in Kelvin angegeben). Je niedriger der Wärmewiderstand, umso besser der Kühlkörper, weil er die gleiche Wärmeleistung mit einem kleineren Temperaturunterschied abführen kann. Dadurch bleibt das Bauteil kühler, was der Lebensdauer und Funktionssicherheit zu Gute kommt.&lt;br /&gt;
&lt;br /&gt;
Allgemein gilt die Formel&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta T = P_t \cdot R_t&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;math&amp;gt;\Delta T&amp;lt;/math&amp;gt; - Temperaturdifferenz zwischen Wärmequelle und Umgebung in K&lt;br /&gt;
*&amp;lt;math&amp;gt;P_t&amp;lt;/math&amp;gt; - Wärmeleistung in W&lt;br /&gt;
*&amp;lt;math&amp;gt;R_t&amp;lt;/math&amp;gt; - Wärmewiderstand in K/W&lt;br /&gt;
&lt;br /&gt;
== Berechnung des Wärmewiderstands ==&lt;br /&gt;
&lt;br /&gt;
Für einen Kühlkörper ist der Wärmewiderstand im Datenblatt angegeben. Diesen rein aus dem Aufbau zu berechnen ist sehr schwierig, auch das Messen ist nicht so einfach. Was man jedoch berechnen kann und muss ist der Wärmewiderstand eines Gesamtaufbaus, d.h. Bauteil + Kühlkörper. Dazu muss man im Wesentlichen zwei Fälle unterscheiden.&lt;br /&gt;
&lt;br /&gt;
=== Bauteil ohne Kühlkörper ===&lt;br /&gt;
&lt;br /&gt;
Ohne Kühlkörper kann ein Bauteil seine Wärme über zwei Wege abgeben. Über das Gehäuse direkt an die Luft (Abstrahlung oder Konvektion) oder über die Anschlüsse auf die Platine (Wärmeleitung). Dies alles findet parallel statt, aber je nach Gehäusetyp und Platinengestaltung ist die Verteilung auf die Kühlwege verschieden. Transistoren im Metallgehäuse (z.&amp;amp;nbsp;B. TO-3) oder mit Metallfahne (z.&amp;amp;nbsp;B. TO220) können recht viel Wärme über das Gehäuse abgeben. Leistungsdioden im Plastikgehäuse hingegen können den Großteil der Wärme nur über die Anschlüsse abgeben. Deshalb sollten diese möglichst kurz sein, und auf der Platine an dicke Leiterbahnen oder gar Kupferflächen angeschlossen werden. Ähnliches gilt für leistungsverstärkte DIL- oder SOIC-[[IC-Gehäuseformen | Gehäuse]], welche oft für [[H-Brücken_Übersicht | Leistungstreiber]] oder [[FET | MOSFETs]] verwendet werden. In diesen Fällen sollten die Pins direkt an Kupferflächen &#039;&#039;&#039;ohne&#039;&#039;&#039; Wärmefallen (Thermals) angeschlossen werden, auch wenn dadurch das [[Löten]] erschwert wird. &lt;br /&gt;
&lt;br /&gt;
Für die meisten Bauteile ist im Datenblatt der Wärmewiderstand zwischen dem eigentlichen Chip (engl. &amp;quot;junction&amp;quot;) und der Umgebung (engl. &amp;quot;ambient&amp;quot;) angegeben. Damit kann man direkt in die oben genannte Formel gehen und die Temperaturdifferenz ausrechnen. Die Temperatur der Sperrschicht errechnet sich einfach aus der maximalen Umgebungstemperatur (meist Luft) und dem errechneten Temperaturunterschied.&lt;br /&gt;
&lt;br /&gt;
=== Bauteil mit Kühlkörper ===&lt;br /&gt;
&lt;br /&gt;
Will man mit einem IC größere Verlustleistungen umsetzen (Linearer Spannungsregler, [[Transistor]], etc.] muss meist ein Kühlkörper her. Die jeweiligen Gehäuse besitzen dazu meist eine Kühlfahne, an die man den Kühlkörper anschrauben kann. Bei anderen gibt es Klemmen, die den Kühlkörper fest klemmen. Hier gibt es einiges zu beachten. Der Kühlkörper darf nicht zu schwach angeschraubt werden, sonst ist der Wärmewiderstand zwischen Gehäuse und Kühlkörper zu gross. Er darf aber auch nicht zu stark angeschraubt/angepresst werden, um das Gehäuse nicht zu deformieren. Wichtig ist der Übergang zwischen IC und Kühlkörper. Hier muss bei größeren Leistungen (&amp;gt;5W) Wärmeleitpaste verwendet werden. Ihre Aufgabe ist es, die Luft zwischen den Oberflächen zu verdrängen, welche sich in den mikroskopischen Unebenheiten befindet und den Wärmewiderstand &#039;&#039;&#039;deutlich&#039;&#039;&#039; erhöht. Dabei sollte die Schicht sehr dünn sein, denn die Wärmeleitpaste ist im Vergleich zu Aluminium oder Kupfer ein schlechter Wärmeleiter, allerdings deutlich besser als Luft.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Material || Wärmeleitfähigkeit&amp;lt;br/&amp;gt;[W/(m*K)]&lt;br /&gt;
|-&lt;br /&gt;
| Luft            || 0,026&lt;br /&gt;
|-&lt;br /&gt;
| Wärmeleitpaste  || 4-10&lt;br /&gt;
|-&lt;br /&gt;
| Aluminium       || 221&lt;br /&gt;
|-&lt;br /&gt;
| Kupfer          || 370&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Das oft verwendete TO220 Gehäuse hat ca. 1cm^2 Kühlfläche. Wird ein Kühlkörper ohne Wärmeleitpaste aufgeschraubt und entsteht dabei ein angenommener Luftspalt von 10µm, hat dieser einen Wärmewiderstand von ca. 4K/W! Mit Wärmeleitpaste sind es rein rechnerisch nur 1/150tel, also etwa 0,026 K/W. Real muss man jedoch eher mit 0,5-1K/W rechnen.&lt;br /&gt;
&lt;br /&gt;
Für die Berechnung des gesamten Wärmewiderstandes müssen hier drei Widerstände in Reihe betrachtet werden. Der Erste ist im Datenblatt zwischen Chip und Gehäuse angeben (engl. junction to case). Danach kommt der Übergang Gehäuse-Kühlkörper. Dieser ist von der Oberflächengüte und der Wärmeleitpaste abhängig und kann nur abgeschätzt werden. Ein TO220 Gehäuse mit dünner Schicht Wärmeleitpaste hat hier ca. 0,5-1K/W. Zum Schluss muss noch der Wärmewiderstand des Kühlkörpers addiert werden, dieser ist im Datenblatt angegeben. Vorsicht, bei größeren Kühlkörpern mit großen Rippen ist die Einbaulage wichtig, damit der Luftstrom frei strömen und gut kühlen kann (freie Konvektion, warme Luft strömt nach oben und kalte strömt unten nach). Die drei Wärmewiderstände werden addiert und über die oben angegebene Formel der Gesamtwärmewiderstand und damit die Temperaturerhöhung der Sperrschicht berechnet.&lt;br /&gt;
&lt;br /&gt;
Wird eine Schaltung in einem Gehäuse eingesetzt, muss man dafür sorgen dass die warme Luft abgeführt wird, vor allem in Kunststoffgehäusen. Ansonsten gibt es einen Wärmestau und die Temperatur steigt deutlich!&lt;br /&gt;
&lt;br /&gt;
=== Zwangskühlung  ===&lt;br /&gt;
&lt;br /&gt;
Natürlich muss der Kühlkörper die Wärme auch abführen können. Was aber, wenn der für den Wärmeabtransport benötigte Kühlkörper mechanisch nicht ins Gehäuse paßt?&lt;br /&gt;
Hier kommen die Lüfter zum Einsatz, das Ganze nennt sich dann Zwangskühlung.&lt;br /&gt;
&lt;br /&gt;
Durch den Einsatz eines Lüfters lässt sich die der Wärmewiderstand des Kühlkörpers etwa um den Faktor 4 verbessern, bzw. der Kühlkörper lässt sich in der Größe ungefähr um den Faktor 7..8 reduzieren. Dies sind jedoch nur Richtwerte für den ersten Entwurf, eine Prüfung durch Messung ist unbedingt erforderlich. Der Effekt beruht darauf, daß mittels eines Lüfters wesentlich mehr Luft wesentlich schneller am Kühlkörper vorbeiströmen kann, und dabei mehr Wärme bei gleicher Temperaturerhöhung aufnehmen kann.&lt;br /&gt;
&lt;br /&gt;
Beim Einsatz eines Lüfters ist auch daran zu denken, daß sowohl die Ansaugöffnung als auch der Kühlkörper verschmutzen und regelmäßig gereinigt werden müssen. Weiterhin erzeugt ein Lüfter natürlich auch Lärm. Je kleiner der Lüfter und je größer die benötigte Luftlieferleistung, umso lauter wird der Lüfter. Umgekehrt kann man aber mit einem großen, eher langsam laufenden Lüfter den Geräuschpegel stark absenken. Und letztendlich kann ein Lüfter auch kaputt gehen, womit die Kühlung deutlich verschlechtert wird und das Bauteil überhitzt. Hier empfiehlt sich bei wertvolleren Objekten eine Lüfterüberwachung, wie sie seit längerem bei PCs eingesetzt wird.&lt;br /&gt;
&lt;br /&gt;
==== Physikalischer Hintergrund der Zwangskühlung mit Luft ====&lt;br /&gt;
&lt;br /&gt;
Luft hat eine Wärmekapazität von ungefähr 1kJ/kg/K was bedeutet, daß für die Erwärmung von 1kg Luft um 1K eine Energiemenge von 1kJ = 1000Ws erforderlich ist. D.h. für den kontinuierlichen Abtransport von 100W Wärme werden mindestens 100g Luft pro Sekunde benötigt, wenn man diese nur um 1K erwärmen will. &lt;br /&gt;
Um also 100W von einem Kühlkörper abzuführen, der sich hier im Beispiel um 8K erwärmen darf, sind  100W / 8K  = 12,5g Luft pro Sekunde erforderlich. Ein Gramm Luft hat ein Volumen von etwa 0,77l, d.h. bei 12,5 g muss der Lüfter 9,6 l/s bzw. 34,5 m^3/h liefern, die dann auch durch den gesamten Kühlkörper geblasen werden müssen.&lt;br /&gt;
&lt;br /&gt;
== Die Platine als Kühlkörper ==&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Leistungen (&amp;lt; 5W) kann man auch die Platine als Kühlkörper benutzten. Dabei muss jedoch die Wärme vom Bauteil möglichst schnell auf eine größere Fläche verteilt werden. Dazu nutzt man große Kupferflächen direkt am Bauteil. Diese werden teilweise beidseitig angebracht. Die Wärme muss man dann jedoch mit vielen Vias von der einen Seite, auf der das Bauteil sitzt, auf die andere geleitet werden. Diese Vias heißen thermische Vias, da sie nicht als elektrische Verbindung sondern als Wärmeleiter dienen. Das funktioniert deshalb so gut, weil die Vias innen mit Kupfer beschichtet sind, welches die Wärme wesentlich besser leitet als das Material der Leiterplatte (FR2, FR4).&lt;br /&gt;
Verfügt ein SMD-Bauteil über eine sogenannte &amp;quot;Heatslug&amp;quot; oder thermal pad auf der Unterseite, muss dieses zur Wärmeableitung unbedingt angelötet werden. Dies ist mit einem normalen Lötkolben möglich, wenn die Platine an dieser Stelle mehrere Durchkontaktierungen mit einem Durchmesser von ca. 1,5mm aufweist. Durch diese Durchkontaktierungen kann genug Lötzinn auf die andere Seite fließen um diese Fläche mit der Platine zu verlöten. &lt;br /&gt;
&lt;br /&gt;
Mit modernen Technologien ist es auch möglich, deutlich größere Wärmeleistungen von der Platine abzuführen. Dazu werden z.&amp;amp;nbsp;B. Platinen mit Aluminium- oder Kupferkern  oder auf einen Metallträger laminierte PCBs benutzt (IMS = &#039;&#039;&#039;I&#039;&#039;&#039;nsulated &#039;&#039;&#039;M&#039;&#039;&#039;etal &#039;&#039;&#039;S&#039;&#039;&#039;ubstrat). Diese kommen z.&amp;amp;nbsp;B. bei Hochleistungs-[[LED]]s zum Einsatz. Für Hobbyzwecke sind sie aber noch wesentlich zu teuer, vor allem bei Einzelstücken.&lt;br /&gt;
&lt;br /&gt;
== Peltierelement ==&lt;br /&gt;
&lt;br /&gt;
Ein [http://de.wikipedia.org/wiki/Peltier-Element Peltierelement] arbeitet nach dem [http://de.wikipedia.org/wiki/Thermoelektrizit%C3%A4t#Peltier-Effekt Peltier-Effekt]. Dabei wird in einem Halbleiter durch Stromfluss eine Seite des Elements kalt, die andere heiß. Damit kann man ein kleines Objekt beliebig kühlen oder heizen. Allerdings sind Peltierelemente nur in eher kleinen Abmessungen und Leistung verfügbar (bis einige Dutzend Watt) und deren Effizienz ist auch nicht sonderlich hoch. Die allgemeine Auffassung, die könnten Wärme einfach verschwinden lassen ist falsch. Denn die heiße Seite muss klassisch gekühlt werden, je nach Temperaturunterschied mit mehr als der doppelten Kühlleistung als auf der kalten Seite an Wärme abgeführt wird.&lt;br /&gt;
&lt;br /&gt;
== Heatpipe ==&lt;br /&gt;
&lt;br /&gt;
Eine [http://de.wikipedia.org/wiki/Heatpipe Heatpipe],auf deutsch Wärmerohr genannt, ist ein Rohr, welches mit einer leicht verdampfenden Flüssigkeit gefüllt ist. Wird ein Ende erhitzt, verdampft die Flüssigkeit und nimmt dabei sehr viel Wärme auf. Der Dampf steigt im Rohr ans andere Ende, kondensiert dort und gibt dabei seine Wärme wieder ab.&lt;br /&gt;
&lt;br /&gt;
Heatpipes werden auch als Wärmesuperleiter bezeichnet, weil sie Wärme 100-10.000 mal besser leiten als ein massiver Kupferstab mit gleichen Abmessungen.&lt;br /&gt;
&lt;br /&gt;
Auch hier muss gesagt werden, dass eine Heatpipe allein &#039;&#039;&#039;kein&#039;&#039;&#039; Kühlsystem ist, denn die kalte Seite muss auch wieder klassisch gekühlt werden. Der grosse Vorteil ist die Abführung großer Wärmemengen auf engstem Raum, wie z.&amp;amp;nbsp;B. bei CPUs in Laptops.&lt;br /&gt;
&lt;br /&gt;
== Flüssigkühlung ==&lt;br /&gt;
&lt;br /&gt;
Im PC-Bereich ist es unter einigen Enthusiasten verbreitet, den Rechner entweder zu übertakten, um eine höhere Leistung zu erzielen oder super leise zu machen, um angenehmer arbeiten oder spielen zu können. In beiden Fällen muss eine große Wärmemenge abgeführt werden. Dabei wird die sehr hohe Wärmekapazität von Wasser genutzt, um auf kleinem Raum die Wärme von CPU, GPU, Festplatten etc. abzuführen. Aber auch hier ist zu beachten, dass am Ende einer Flüssigkühlung praktisch immer ein klassicher Wärmetauscher steht, welcher die Wärme an die Umgebung Luft abgibt. Dieser kann sich aber deutlich weiter entfernt vom zu kühlenden Objekt befinden als ein einfacher, direkt montierter Kühlkörper. &lt;br /&gt;
&lt;br /&gt;
Bei der Verwendung von Wasser statt Luft als Kühlmedium reduziert sich die Durchflußmasse in etwa um den Faktor 4,2, da die Wärmekapazität von Wasser bei ca. 4,182 kJ/kg/K liegt. Da Wasser aber auch eine deutlich höhere Dichte als Luft besitzt (Wasser = 1g/cm³; Luft = 1,3mg/cm³) kommt noch der Faktor von ~770 dazu, woraus sich ein Gesamtfaktor für das Durchflußvolumen von ~3230 ergibt.&lt;br /&gt;
&lt;br /&gt;
D.h. die Durchflußmenge in unserem oben genannten Beispiel (100W) sinkt auf ca. 2,9 ml/s bzw. 10,7 l/h.&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
*Abtransport großer Wärmemengen auf kleinstem Raum&lt;br /&gt;
*nahezu lautlos&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
*höherer Aufwand und Kosten&lt;br /&gt;
*Gefahr durch auslaufendes Kühlmittel&lt;br /&gt;
&lt;br /&gt;
Im Bereich der Leistungselektronik wird Flüssigkühlung eingesetzt, im Hobbybereich nahezu nicht.&lt;br /&gt;
&lt;br /&gt;
== Weitere Hinweise ==&lt;br /&gt;
&lt;br /&gt;
*Große Hochlastwiderstände mit Keramikgehäuse werden im Normalbetrieb recht heiß (200..350°C). Diese Temperaturen sollten nicht auf die Platine kommen, denn das macht das Material nicht lange mit. Hier muss genau das Gegenteil von dem gemacht werden, was weiter oben für Bauteile ohne Kühlkörper empfohlen wurde. Die Anschlüsse müssen möglichst lang sein, damit wenig Wärme über sie abgegeben werden kann. Die Kühlung erfolgt nahezu nur über den Keramikkörper.&lt;br /&gt;
* Ein TO220 Gehäuse kann ca. 1W ohne Kühlkörper abgeben.&lt;br /&gt;
* Bei der Dimensionierung des Kühlkörpers sollte man sich nicht an der maximal zulässigen Sperrschichttemperatur orientieren, sondern möglichst um 10..50K kühler bleiben. Das verbessert die Funktionssicherheit und vor allem die Lebensdauer erheblich!&lt;br /&gt;
* Merksatz über den dicken Daumen: Pro 10 Grad Temperaturerhöhung halbiert sich die Lebenserwartung eines Bauteils.&lt;br /&gt;
* Meist sind mehrere kleine Kühlkkörper deutlich kleiner und billiger als ein Großer.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* [[Treiber]]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
*[http://sound.westhost.com/heatsinks.htm Eine ausführliche Seite zum Thema Kühlkörper, englisch]&lt;br /&gt;
*[http://ludens.cl/Electron/Thermal.html Noch eine gute Seite, englisch]&lt;br /&gt;
*[http://wiki.oliverbetz.de/owiki.php/FormelSammlung Universelle Formelsammlung mit kurzen Erklärungen]&lt;br /&gt;
*[http://www.leiton.de/leiterplatten_teaser_alu.html Leiterplatten mit Alukern bei Leiton]&lt;br /&gt;
*[http://www.thermoconsult.de/01_TechInfo/Physik.pdf PDF Dokument mit Grundlagen zur Kühlung, inhaltlich gut, Formatierung eher mies]&lt;br /&gt;
*[http://www.fischerelektronik.de/ Kühlkörper bei Fischer Elektronik]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]]&lt;br /&gt;
[[Kategorie:Leistungselektronik]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28257</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28257"/>
		<updated>2008-05-27T17:39:37Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Allgemeiner Zugriff auf Register */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[AVR-GCC]] erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Progammiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong]. Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern, weder in Assembler noch in einer anderen Sprache. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR Controllern finden. Bei WinAVR gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden stetig weiterentwickelt. Erläuterungen und Beispiele beziehen sich auf den C-Compiler avr-gcc ab Version 3.4 und die avr-libc ab Version 1.4.3. Die Unterschiede zu älteren Versionen werden im Haupttext und Anhang zwar erläutert, Anfängern sei jedoch empfohlen, die aktuellen Versionen zu nutzen (für MS-Windows: aktuelle Version des [[WinAVR]]-Pakets). &lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in PDF-Form erhältlich bei:&lt;br /&gt;
http://www.siwawi.arubi.uni-kl.de/avr_projects/AVR-GCC-Tutorial_-_www_mikrocontroller_net.pdf&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels avr-gcc/avr-libc zu erstellen und zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR Controllers, der vom avr-gcc Compiler unterstützt wird (alle ATmegas und die meisten AT90, siehe Dokumentation der avr-libc für unterstützte Typen). Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man [[AVR#Starterkits|hier]].&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc Compiler und die avr-libc. Kostenlos erhältlich für nahezu alle Plattformen und Betriebssysteme. Für MS-Windows im Paket [[WinAVR]]; für Unix/Linux siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]]).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder nur die eigenen C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
&lt;br /&gt;
* Die [[AVR Checkliste]] durcharbeiten.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/2 GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  auf den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
&lt;br /&gt;
* Die Beispieleprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweisen sind aber auch auf C-Programme übertragbar.&lt;br /&gt;
&lt;br /&gt;
* Einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien, um das Problem nachzuvollziehen, sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z. B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.&amp;amp;nbsp;B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.&amp;amp;nbsp;B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen, um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu zwei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer integrierten Entwicklungsumgebung (IDE = &#039;&#039;&#039;I&#039;&#039;&#039;ntegrated &#039;&#039;&#039;D&#039;&#039;&#039;evelopment &#039;&#039;&#039;E&#039;&#039;&#039;nvironment), bei der alle Einstellungen z.&amp;amp;nbsp;B. in Dialogboxen durchgeführt werden können. Unter Anderem kann AVRStudio ab Version 4.12 (kostenlos auf [http://www.atmel.com/ atmel.com]) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs für den avr-gcc (ohne Anspruch auf Vollständigkeit): AtmanAvr C (relativ günstig), KamAVR (kostenlos), VMLab (ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.&amp;amp;nbsp;B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand der universellen und plattformunabhängigen Vorgehensweise mittels make und Makefiles näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind lediglich Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xff;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5a)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/;  // (5b)&lt;br /&gt;
   }                         // (5c)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (6)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In der mit (1) markierten Zeile wird eine so genannte Header-Datei eingebunden. In io.h sind die Registernamen definiert, die im späteren Verlauf genutzt werden.&lt;br /&gt;
&lt;br /&gt;
* Bei (2) beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion main.&lt;br /&gt;
&lt;br /&gt;
* Die Anschlüsse eines AVR (&amp;quot;Beinchen&amp;quot;) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Richtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B zu Ausgängen.&lt;br /&gt;
&lt;br /&gt;
* (4) stellt die Werte der Ausgänge ein. Die den ersten beiden Bits des Ports zugeordneten Anschlüsse (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential).&lt;br /&gt;
&lt;br /&gt;
* (5) ist die so genannte Hauptschleife (main-loop). Dies ist eine Programmschleife, welche kontinuierlich wiederkehrende Befehle enthält. In diesem Beispiel ist sie leer. Der Controller durchläuft die Schleife immer wieder, ohne dass etwas passiert (außer das Strom verbraucht wird). Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Ohne diese Schleife wäre der Zustand des Controllers nach dem Programmende undefiniert.&lt;br /&gt;
&lt;br /&gt;
* (6) wäre das Programmende. Die Zeile ist nur aus Gründen der C-Kompatibilität enthalten: int main(void) besagt, dass die Funktion einen Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein auf dem Controller lauffähiges Programm zu übersetzen, wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen Makefile (ohne Endung) im selben Verzeichnis, in dem auch die Datei main.c mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im folgenden Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\tmp\gcc_tut\quickstart&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei main.hex, in der der Code für den AVR enthalten ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.&amp;amp;nbsp;B. über In-System-Programming (ISP) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann (z.&amp;amp;nbsp;B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio), kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine LED leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode mit einem dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Exkurs: Makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebungen à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Plattformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt, genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.com) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das Makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den im Makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im Makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten Makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt [[AVR-GCC-Tutorial#EEPROM|Speicherzugriffe]]), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendeten Controllers gesetzt. Eine Liste der von avr-gcc und der avr-libc unterstützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien eintragen ==&lt;br /&gt;
&lt;br /&gt;
Der Name der Quellcodedatei, welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.&amp;amp;nbsp;B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
# nicht auskommentiert: EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Das erstellte Makefile und der Code müssen im gleichen Ordner sein, auch sollte der Dateiname nicht verändert werden. &lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem Makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschinencode erzeugt. Beim Update auf eine neue Compilerversion ist zu beachten, dass diese möglicherweise intern andere Optimierungsalgorithmen verwendet und sich dadurch die Größe des Machinencodes etwas ändert, ohne dass man im Quellcode etwas geändert hat.&lt;br /&gt;
&lt;br /&gt;
Als Orientierungswerte die Größe des Maschinencodes bei verschiedenen Optionen für einen nicht näher spezifizierten relativ kleinen Testcode bei Verwendung einer nicht näher spezifizierten Compilerversion. &lt;br /&gt;
&lt;br /&gt;
* -O0 : 12&#039;217 Byte&lt;br /&gt;
&lt;br /&gt;
* -O1 : 9&#039;128 Byte&lt;br /&gt;
&lt;br /&gt;
* -O2 : 1&#039;670 Byte&lt;br /&gt;
&lt;br /&gt;
* -O3 : 3&#039;004 Byte&lt;br /&gt;
&lt;br /&gt;
* -Os : 1&#039;695 Byte&lt;br /&gt;
&lt;br /&gt;
Im diesem Testfall führt die Option -O2 mit zum kompaktesten Code, dies  allerdings hier nur mit 25 Bytes &amp;quot;Vorsprung&amp;quot;. Es kann durchaus sein, dass nur wenige Programmerweiterungen dazu führen, dass Compilieren mit -Os wieder in kompakteren Code resultiert.&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/using_tools.html#gcc_optO avr-libc manual Abschnitt Using the gnu-tools/Compiler-Optionen]&lt;br /&gt;
&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_optflags avr-libc Manual FAQ Nr. 16] (Stand avr-libc Version 1.4.5)&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wird hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (großes S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten, lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Taktfrequenz ===&lt;br /&gt;
&lt;br /&gt;
Neuere Versionen der WinAVR/Mfile Vorlage für Makefiles beinhalten die Definition einer Variablen F_CPU (WinAVR 2/2005). Darin wird die Taktfrequenz des Controllers in Hertz eingetragen. Die Definition steht dann im gesamten Projekt ebenfalls unter der Bezeichnung F_CPU zur Verfügung (z.&amp;amp;nbsp;B. um daraus UART-, SPI- oder ADC-Frequenzeinstellungen abzuleiten).&lt;br /&gt;
&lt;br /&gt;
Die Angabe hat rein &amp;quot;informativen&amp;quot; Charakter, die tatsächliche Taktrate wird über den externen Takt (z.&amp;amp;nbsp;B. Quarz) bzw. die Einstellung des internen R/C-Oszillators  bestimmt. Die Nutzung von F_CPU hat also nur Sinn, wenn die Angabe mit dem tatsächlichen Takt übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Innerhalb neuerer Versionen der avr-libc (ab Version 1.2) wird die Definition der Taktfrequenz (F_CPU) zur Berechnung der Wartefunktionen in delay.h genutzt. Diese funktionieren nur dann korrekt, wenn F_CPU mit der tatsächlichen Taktfrequenz übereinstimmt.&lt;br /&gt;
F_CPU muss dazu jedoch nicht unbedingt im makefile definiert werden. Es reicht aus, wird aber bei mehrfacher Anwendung unübersichtlich, vor &#039;&#039;#include &amp;lt;util/delay.h&amp;gt;&#039;&#039; (veraltet: &#039;&#039;#include &amp;lt;avr/delay.h&amp;gt;&#039;&#039;) ein &#039;&#039;#define F_CPU [hier Takt in Hz]UL&#039;&#039; einzufügen. Bei Nutzung von delay.h ist darauf zu achten, dass die Optimierung des Compilers nicht ausgeschaltet ist, sonst wird sehr viel Code erzeugt und die Wartezeit stimmt nicht mit der gewünschten Zeitspanne überein. Vgl. dazu den [http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html entsprechenden Abschnitt der Dokumentation].&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage so genannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* Im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
* Die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* Weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff: (sollte nur noch in Ausnahmefällen genutzt werden)&lt;br /&gt;
&lt;br /&gt;
* Im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
* Die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* Weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler diese &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, einem Nachbau davon (BootICE, Evertool o.&amp;amp;nbsp;ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
Die Verwendung von Makefiles bietet noch viele weitere Möglichkeiten, einige davon werden im Anhang [[AVR-GCC-Tutorial#Zus.C3.A4tzliche_Funktionen_im_Makefile|Zusätzliche Funktionen im Makefile]] erläutert.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige (Integer) Datentypen =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei stdint.h definiert. &lt;br /&gt;
Zur Nutzung der standardisierten Typen bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// ab avr-libc Version 1.2.0 möglich und empfohlen:&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// veraltet: #include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef signed char        int8_t;&lt;br /&gt;
typedef unsigned char      uint8_t;&lt;br /&gt;
&lt;br /&gt;
typedef short              int16_t;&lt;br /&gt;
typedef unsigned short     uint16_t;&lt;br /&gt;
&lt;br /&gt;
typedef long               int32_t;&lt;br /&gt;
typedef unsigned long      uint32_t;&lt;br /&gt;
&lt;br /&gt;
typedef long long          int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* int8_t steht für einen 8-Bit Integer mit einem Wertebereich -128 bis +127.&lt;br /&gt;
&lt;br /&gt;
* uint8_t steht für einen 8-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 255&lt;br /&gt;
&lt;br /&gt;
* int16_t steht für einen 16-Bit Integer mit einem Wertebereich -32768 bis +32767.&lt;br /&gt;
&lt;br /&gt;
* uint16_t steht für einen 16-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 65535.&lt;br /&gt;
&lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; werden als vorzeichenbehaftete Zahlen abgespeichert. Typen mit vorgestelltem &#039;&#039;u&#039;&#039; dienen der Ablage von postiven Zahlen (inkl. 0). Siehe dazu auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/(Standard) Integer Types.&lt;br /&gt;
&lt;br /&gt;
= Bitfelder =&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bits breit ist.&lt;br /&gt;
&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in eine einzelne Bytevariable zusammenfassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können. Die Rede ist von so genannten Bitfeldern. Diese werden als Strukturelemente definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitfelder sparen Platz im RAM, zu Lasten von Platz im Flash, verschlechtern aber unter Umständen die Les- und Wartbarkeit des Codes. Anfängern wird deshalb geraten, ein &amp;quot;ganzes&amp;quot; Byte (uint8_t) zu nutzen, auch wenn nur ein Bitwert gespeichert werden soll.&lt;br /&gt;
&lt;br /&gt;
Wenn man nur ein paar wenige Variablen vom Typ bool verwenden möchte, kann man&lt;br /&gt;
auch die Headerdatei &amp;lt;stdbool.h&amp;gt; einbinden und sich dann wie gewohnt einen Booltyp anlegen. Variablen dieses Typs brauchen dennoch 1 Byte Speicher, ermöglichen aber eine genaue Unterscheidung zwischen Zahlenvariable und boolscher Variable.&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;C-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher Dinge erledigt werden können, welche nicht zeitkritisch sind. Wenn ein Interrupt ausgelöst wird, so wird automatisch die zugeordnete Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum können für allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind, selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.&amp;amp;nbsp;B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039; wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern. Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers. Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output). Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt. Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend angepasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O-Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek (avr-libc) stellt auch Funktionen zur Abfrage eines einzelnen Bits eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;): Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist, wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;): Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind &#039;&#039;nicht erforderlich&#039;&#039;, man kann auch &amp;quot;einfache&amp;quot; C-Syntax verwenden, die universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.&amp;amp;nbsp;B. (Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt; 0 (&amp;quot;wahr&amp;quot;), wenn das Bit gesetzt ist und 0 (&amp;quot;falsch&amp;quot;), wenn es nicht gesetzt ist. Die Negierung des Ausdrucks, also  &#039;&#039;&#039;!&#039;&#039;&#039;(Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)), entspricht &#039;&#039;bit_is_clear&#039;&#039; und gibt einen Wert &amp;lt;&amp;gt; 0 (&amp;quot;wahr&amp;quot;) zurück, falls das Bit nicht gesetzt ist. Siehe auch [[Bitmanipulation]].&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O-Register einfach wie eine Variable setzen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und outp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben von Bits ====&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // Hiermit wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // Hiermit wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit eines Bytes ist Bit 0, das höchstwertigste Bit 7.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );    /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3);   /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die warten, bis ein bestimmter Zustand auf einem Bit erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend gewartet&amp;quot; wird. Der Programmablauf bleibt also an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den Watchdog ein, muss man darauf achten, dass dieser auch noch getriggert wird (Zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Änderung: Habe PINx und PORTx vertauscht.PINx ist für Pull up und PORTx für high bzw low. Michael.L  - &lt;br /&gt;
&lt;br /&gt;
Was sollte diese Aenderung?? Den Rest des Abschnitts auch gelesen? Evtl. die hier nicht erlaeuterten Zusatzfunktion der neuen AVRs durcheinandergeworfen? Aenderung verworfen bis mehr Details.  m.thomas. &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
// Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
&lt;br /&gt;
DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
/* mehr Tipparbeit aber übersichtlicher: */&lt;br /&gt;
DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&lt;br /&gt;
/* Einige Compiler erlauben die Eingabe von Konstanten im &lt;br /&gt;
   Binärformat, z.B. gcc in WinAVR, NICHT aber aus&lt;br /&gt;
   GNU-gcc-Quellen selbst erstellte Compiler. Diese Schreib-&lt;br /&gt;
   weise also nicht nutzen, wenn Code mit anderen &lt;br /&gt;
   ausgetauscht werden soll. */&lt;br /&gt;
DDRB = 0b00011111;    /* direkte Zuweisung &lt;br /&gt;
                         - übersichtlicher &lt;br /&gt;
                         - nicht standardkonform &lt;br /&gt;
                         - selten portabel&lt;br /&gt;
                         - nur bei modifiziertem GCC */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
Weitere Beispiele:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
DDRB = 0xff; &lt;br /&gt;
// Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; DDB0 );&lt;br /&gt;
// Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( ( 1 &amp;lt;&amp;lt; DDB3 ) | ( 1&amp;lt;&amp;lt;DDB4) );&lt;br /&gt;
// Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB |= ( 1 &amp;lt;&amp;lt; DDB0) | ( 1 &amp;lt;&amp;lt; DDB3 );&lt;br /&gt;
// Alle Pins auf Eingang:&lt;br /&gt;
DDRB = 0x00;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&#039;&#039;&lt;br /&gt;
* &#039;&#039;zuerst die Bits im PORTx-Register setzen und&#039;&#039;&lt;br /&gt;
* &#039;&#039;anschließend die Datenrichtung auf Ausgang stellen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Daraus ergibt sich die Abfolge für einen Eingangspin:&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze PORTx: interner Pull-Up aktiv&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze DDRx: Ausgang &amp;quot;high&amp;quot;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bei der Reihenfolge erst DDRx und dann PORTx, kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;&#039;nicht&#039;&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, &#039;&#039;&#039;sondern&#039;&#039;&#039; Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.&amp;lt;/font&amp;gt; (Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände.)&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch.  Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVRs haben sogar an den meisten Pins softwaremäßig zuschaltbare interne Pull-Up Widerstände, welche wir natürlich auch verwenden können.&lt;br /&gt;
&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;DDC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== (Tasten-)Entprellung ====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim Schließen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&lt;br /&gt;
&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen als mehrfache Impulse gezählt wird. Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* bei alter avr-libc: #include &amp;lt;avr/delay.h&amp;gt; */      &lt;br /&gt;
&lt;br /&gt;
/* Einfache Funktion zum Entprellen eines Tasters */&lt;br /&gt;
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)&lt;br /&gt;
{&lt;br /&gt;
    if ( ! (*port &amp;amp; (1 &amp;lt;&amp;lt; pin)) )&lt;br /&gt;
    {&lt;br /&gt;
        /* Pin wurde auf Masse gezogen, 100ms warten   */&lt;br /&gt;
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz&lt;br /&gt;
        _delay_ms(50); &lt;br /&gt;
        if ( *port &amp;amp; (1 &amp;lt;&amp;lt; pin) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Anwender Zeit zum Loslassen des Tasters geben */&lt;br /&gt;
            _delay_ms(50);&lt;br /&gt;
            _delay_ms(50); &lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; PB0 );                 /* PIN PB0 auf Eingang (Taster)            */&lt;br /&gt;
    PORTB |= ( 1 &amp;lt;&amp;lt; PB0 );                 /* Pullup-Widerstand aktivieren            */&lt;br /&gt;
    ...&lt;br /&gt;
    if (debounce(&amp;amp;PINB, PB0))             /* Falls Taster an PIN PB0 gedrueckt..    */&lt;br /&gt;
        PORTD = PIND ^ ( 1 &amp;lt;&amp;lt; PD7 );  /* ..LED an Port PD7 an-&lt;br /&gt;
                                   bzw. ausschalten */&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesem Beispiel ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritische Anwendungen sollte man ein anderes Verfahren nutzen (z.B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).&lt;br /&gt;
&lt;br /&gt;
Zum Thema Entprellen siehe auch:&lt;br /&gt;
* Artikel [[Entprellung]]&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls ben&amp;amp;ouml;tigt, kann eine 16-Bit Variable auch recht einfach manuell in ihre zwei 8-Bit Bestandteile zerlegt werden. Folgendes Beispiel demonstriert dies anhand des pseudo- 16-Bit Registers UBRR.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
typedef union {&lt;br /&gt;
        uint16_t i16;&lt;br /&gt;
        struct {&lt;br /&gt;
                uint8_t i8l;&lt;br /&gt;
                uint8_t i8h;&lt;br /&gt;
        };&lt;br /&gt;
} convert16to8;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
convert16to8 baud;&lt;br /&gt;
baud.i16 = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
UBRRH = baud.i8h;&lt;br /&gt;
UBRRL = baud.i8l;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/*alternativ:*/&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint16_t wFoo16;&lt;br /&gt;
uint8_t bFooLow, bFooHigh;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
wFoo16   = 0xAA55;                 /* zu &amp;quot;zerlegende&amp;quot; 16Bit-Integer */&lt;br /&gt;
bFooHigh = (uint8_t)(wFoo16 &amp;gt;&amp;gt; 8); /* MS-Byte */&lt;br /&gt;
bFooLow  = (uint8_t)(wFoo16);      /* LS-Byte */&lt;br /&gt;
...&lt;br /&gt;
#define us0(Data) ((unsigned char *)(&amp;amp;Data))&lt;br /&gt;
#define us1(Data) ((unsigned char *)((&amp;amp;Data)+1))&lt;br /&gt;
bFooHigh = us1(wFoo16);&lt;br /&gt;
bFoolow  = us0(wFoo16);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen (z.B. ATmega8) teilen sich UBRRH und UCSRC die gleiche Memory-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL) welches Register tats&amp;amp;auml;chlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und &amp;amp;uuml;bergibt den Wert &#039;&#039;3&#039;&#039; und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und &amp;amp;uuml;bergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Bei einigen 16-bit Registern (insbesondere die der 16-bit Timer) erfolgen Schreibzugriffe über das sogenannte &#039;&#039;temporary Register&#039;&#039;. Die Reihenfolge der Zugriffe bestimmt, wann der Wert tatsächlich ins Register geschrieben wird. Typisch wird erst das High-Byte beschrieben (xxxH) und intern im temporary Register zwischengespeichert. Nachdem das Low-Byte (xxxL) geschrieben wurde, setzt der Controller mit diesem und dem im temporary Register zwischengespeicherten Wert für das High-Byte das 16-bit Register. Dabei ist zu beachten, dass intern nur ein temporary Register verfügbar ist, welches in Interruptroutinen mglw. mit einem anderen Wert überschrieben wird, wenn dort ebenfalls 16-bit Register beschrieben werden. avr-gcc/avr-libc berücksichtigen die korrekte Reihenfolge automatisch, wenn die Register mit ihrem &amp;quot;16-bit Label&amp;quot; (ohne H bzw. L) angesprochen werden, dabei ist der Schutz des temporary Registers vor Überschreiben durch Interruptroutinen dennoch zu beachten (im Zweifel beim Schreibzugriff die Interrupts kurzzeitig global deaktivieren).&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t key_pressed(const volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if ( last_state == ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) ) ) {&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    i = key_pressed( &amp;amp;PINB, PB1 );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Im Übrigen können mit volatile gekennzeichnete Variablen auch als const deklariert werden, um sicherzustellen, dass sie nur noch von der Hardware änderbar sind.&lt;br /&gt;
&lt;br /&gt;
= Der UART =&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Über den [[UART]] kann ein AVR leicht mit einer [[RS-232]]-Schnittstelle eines PC oder sonstiger Geräte mit &amp;quot;serieller Schnittstelle&amp;quot; verbunden werden. &lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungen des UART:&lt;br /&gt;
&lt;br /&gt;
* Debug-Schnittstelle: z.B. zur Anzeige von Zwischenergebnissen (&amp;quot;printf-debugging&amp;quot; - hier besser &amp;quot;UART-debugging&amp;quot;) auf einem PC. Auf dem Rechner reicht dazu ein [[RS-232#Terminalprogramme|Terminalprogramm]] (MS-Windows: Hyperterm oder besser Bray-Terminal, [http://www.mikrocontroller.net/forum/read-8-155472.html#new HTerm]; Unix/Linux z.B. minicom). Ein direkter Anschluss ist aufgrund unterschiedlicher Pegel nicht möglich, jedoch sind entsprechende Schnittstellen-ICs wie z.B. ein MAX232 günstig und leicht zu integrieren. Rechner ohne serielle Schnittstelle können über fertige USB-seriell-Adapter angeschlossen werden. &lt;br /&gt;
* &amp;quot;Mensch-Maschine Schnittstelle&amp;quot;: z.B. Konfiguration und Statusabfrage über eine &amp;quot;Kommandozeile&amp;quot; oder Menüs (siehe z.B. Forumsbeitrag [http://www.mikrocontroller.net/topic/52985 Auswertung RS232-Befehle]) &lt;br /&gt;
* Übertragen von gespeicherten Werten: z.B. bei einem Datenlogger&lt;br /&gt;
* Anschluss von Geräten mit serieller Schnittstelle (z.B. (Funk-)Modems, Mobiltelefone, Drucker, Sensoren, &amp;quot;intelligente&amp;quot; LC-Displays, GPS-Empfänger). &lt;br /&gt;
* &amp;quot;Feldbusse&amp;quot; auf RS485/RS422-Basis mittels entsprechenden Bustreiberbausteinen (z.B. MAX485)&lt;br /&gt;
* DMX, Midi etc.&lt;br /&gt;
* LIN-Bus (&#039;&#039;&#039;L&#039;&#039;&#039;ocal &#039;&#039;&#039;I&#039;&#039;&#039;nterconnect &#039;&#039;&#039;N&#039;&#039;&#039;etwork): Preiswerte Sensoren/Aktoren in der Automobiltechnik und darüber hinaus&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben ein bis zwei vollduplexfähigen UART (&#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) schon eingebaut (&amp;quot;Hardware-UART&amp;quot;). &lt;br /&gt;
Übrigens: Vollduplex heißt nichts anderes, als dass der Baustein gleichzeitig senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega, ATtiny) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterscheidet sich vom UART hauptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurationsregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit zeigt an, ob der Sendepuffer bereit ist, um ein zu sendendes Zeichen aufzunehmen. Das Bit wird vom AVR gesetzt (1), wenn der Sendepuffer leer ist. Es wird gelöscht (0), wenn ein Zeichen im Sendedatenregister vorhanden ist und noch nicht in das Sendeschieberegister übernommen wurde. Atmel empfiehlt aus Kompatibilitätsgründen mit kommenden µC, UDRE auf 0 zu setzen, wenn das UCSRA Register beschrieben wird.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
\mathrm{UBRR} = \frac{\mathrm{Taktfrequenz}}{\mathrm{Baudrate} \cdot 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS-232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. &lt;br /&gt;
&amp;lt;!-- &amp;quot;Hackerloesung&amp;quot; auskommentiert - nicht so gut in einem &amp;quot;Einsteiger-Tutorial&amp;quot; - mthomas&lt;br /&gt;
Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfiehlt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== UART initialisieren ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben. Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach gewünschter Funktionsweise die benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Da wir vorerst nur senden möchten und noch keine Interrupts auswerten wollen, gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs mit USART haben mehrere Konfigurationsregister und erfordern eine etwas andere Konfiguration. Für einen ATmega16 z.B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                 // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);     // Asynchron 8N1&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist noch das Baudratenregister &#039;&#039;&#039;UBRR&#039;&#039;&#039; einzustellen. Bei neueren AVRs besteht es aus zwei Registern &#039;&#039;&#039;UBRRL&#039;&#039;&#039; und &#039;&#039;&#039;UBRRH&#039;&#039;&#039;. Der Wert dafür ergibt sich aus der angegebenen Formel durch Einsetzen der Taktfrequenz und der gewünschten Übertragungsrate. Das Berechnen der Formel wird dem [[C-Präprozessor|Präprozessor]] überlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann&lt;br /&gt;
   F_CPU im Makefile definiert werden, eine nochmalige Definition&lt;br /&gt;
   hier wuerde zu einer Compilerwarnung fuehren. Daher &amp;quot;Schutz&amp;quot; durch&lt;br /&gt;
   #ifndef/#endif &lt;br /&gt;
&lt;br /&gt;
   Dieser &amp;quot;Schutz&amp;quot; kann zu Debugsessions führen, wenn AVRStudio &lt;br /&gt;
   verwendet wird und dort eine andere, nicht zur Hardware passende &lt;br /&gt;
   Taktrate eingestellt ist: Dann wird die folgende Definition &lt;br /&gt;
   nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) &lt;br /&gt;
   von AVRStudio - daher Ausgabe einer Warnung falls F_CPU&lt;br /&gt;
   noch nicht definiert: */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000&amp;quot;&lt;br /&gt;
#define F_CPU 4000000UL    // Systemtakt in Hz &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600UL          // Baudrate&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) // Fehler in Promille, 1000 = kein Fehler.&lt;br /&gt;
&lt;br /&gt;
#if ((BAUD_ERROR&amp;lt;990) || (BAUD_ERROR&amp;gt;1010))&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;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    UBRR = UBRR_VAL;&lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&amp;lt;!-- mthomas: warum nicht UL?, wird von AVRStudio auch mit UL übergeben --&amp;gt;&lt;br /&gt;
Wieder für den Mega16 mit zwei Registern für die Baudrateneinstellung eine etwas andere Programmierung. Wichtig ist, dass UBRRH &#039;&#039;&#039;vor&#039;&#039;&#039; UBRRL geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmega16 */&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einige AVR (z.B. ATmega169, ATmega48/88/168, AT90CAN jedoch nicht für z.B. ATmega16/32, ATmega128, ATtiny2313) wird durch die Registerdefinitionen der avr-libc (io*.h) auch für Controller mit zwei UBRR-Registern (UBRRL/UBRRH) ein UBRR bzw. UBRR0 als &amp;quot;16-bit-Register&amp;quot; definiert und man kann auch Werte direkt per UBRR = UBRR_VAL zuweisen. Intern werden dann zwei Zuweisungen für UBRRH und UBRRL generiert. Dies ist nicht bei allen Controllern möglich, da die beiden Register nicht bei allen aufeinanderfolgende Addressen aufweisen. Die getrennte Zuweisung an UBRRH und UBRRL wie im Beispiel gezeigt ist universeller und portabler und daher vorzuziehen.&lt;br /&gt;
&lt;br /&gt;
Die Makros sind sehr praktisch, da sie sowohl automatisch den Wert für UBRR als auch den Fehler in der generierten Baudrate berechnen und im Falle einer Überschreitung (+/-1%) einen Fehler und somit Abbruch im Compilerablauf generieren. Damit können viele Probleme mit &amp;quot;UART sendet komische Zeichen&amp;quot; vermieden werden. Ausserdem kann man mühelos die Einstellung an eine neue Taktfrequenz bzw. Baudrate anpassen, ohne selber rechnen oder in Tabellen nachschlagen zu müssen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. Vorher ist zu prüfen, ob das UART-Modul bereit ist, das zu sendende Zeichen entgegenzunehmen. Die Bezeichnungen des/der Statusregisters mit dem Bit UDRE ist abhängig vom Controllertypen (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    // bei AVR mit einem UART (&amp;quot;classic AVR&amp;quot; z.B. AT90S8515)&lt;br /&gt;
    while (!(USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                  /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&lt;br /&gt;
    /** ODER **/&lt;br /&gt;
&lt;br /&gt;
    // bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                    /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe &amp;quot;String senden&amp;quot; wird durch zwei Funktionen abgearbeitet. Die universelle/controllerunabhängige Funktion uart_puts übergibt jeweils ein Zeichen der Zeichenkette an eine Funktion uart_putc, die abhängig von der vorhandenen Hardware implementiert werden muss. In der Funktion zum Senden eines Zeichens ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// putc fuer AVR mit einem UART (z.B. AT90S8515)&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while(!(USR &amp;amp; (1 &amp;lt;&amp;lt; UDRE)))  /* warte, bis UDR bereit */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = c;                     /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** ODER **/&lt;br /&gt;
&lt;br /&gt;
// bei neueren AVRs andere Bezeichnung fuer die Statusregister, hier ATmega16:&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich */&lt;br /&gt;
    {&lt;br /&gt;
    }                             &lt;br /&gt;
&lt;br /&gt;
    UDR = c;                      /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* puts ist unabhaengig vom Controllertyp */&lt;br /&gt;
void uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != &#039;\0&#039; also ungleich dem &amp;quot;String-Endezeichen&amp;quot; */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die in uart_putc verwendeten Schleifen, in denen gewartet wird bis die UART-Hardware zum senden bereit ist, sind insofern etwas kritisch, da während des Sendens eines Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. empfangenen Zeichen/Bytes zwischengespeichert und in Interruptroutinen an die U(S)ART-Hardware weitergegeben bzw. von ihr ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&lt;br /&gt;
&lt;br /&gt;
=== Schreiben von Variableninhalten ===&lt;br /&gt;
&lt;br /&gt;
Sollen Inhalte von Variablen (Ganzzahlen, Fließkomma) in &amp;quot;menschenlesbarer&amp;quot; Form gesendet werden, ist vor dem Transfer eine Umwandlung in Zeichen (&amp;quot;ASCII&amp;quot;) erforderlich. Bei nur einer Ziffer ist diese Umwandlung relativ einfach: man addiert den ASCII-Wert von Null zur Ziffer und kann diesen Wert direkt senden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_putc (s.o.)&lt;br /&gt;
&lt;br /&gt;
// Ausgabe von 0123456789&lt;br /&gt;
char c;&lt;br /&gt;
&lt;br /&gt;
for (uint8_t i=0; i&amp;lt;=9; ++i) {&lt;br /&gt;
    c = i + &#039;0&#039;;&lt;br /&gt;
    uart_putc( c );&lt;br /&gt;
    // verkuerzt: uart_putc( i + &#039;0&#039; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll mehr als eine Ziffer ausgegeben werden, bedient man sich zweckmäßigerweise vorhandener Funktionen zur Umwandlung von Zahlen in Zeichenketten/Strings. Die Funktion der avr-libc zur Umwandlung von vorzeichenbehafteten 16bit-Ganzzahlen (int16_t) in Zeichenketten heißt &#039;&#039;itoa&#039;&#039; (Integer to ASCII). Man muss der Funktion einen Speicherbereich zur Verarbeitung (buffer) mit Platz für alle Ziffern, das String-Endezeichen (&#039;\0&#039;) und evtl. das Vorzeichen bereitstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   char s[7];&lt;br /&gt;
   int16_t i = -12345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   itoa( i, s, 10 ); // 10 fuer radix -&amp;gt; Dezimalsystem&lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // da itoa einen Zeiger auf den Beginn von s zurueckgibt verkuerzt auch:&lt;br /&gt;
   uart_puts( itoa( i, s, 10 ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für vorzeichenlose 16bit-Ganzzahlen (uint16_t) exisitert &#039;&#039;utoa&#039;&#039;. Die Funktionen für 32bit-Ganzzahlen (int32_t und uint32_t) heißen &#039;&#039;ltoa&#039;&#039; bzw. &#039;&#039;ultoa&#039;&#039;. Da 32bit-Ganzzahlen mehr Stellen aufweisen können, ist ein entsprechend größerer Pufferspeicher vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Auch Fließkommazahlen (float/double) können mit breits vorhandenen Funktionen in Zeichenfolgen umgewandelt werden, dazu existieren die Funktionen &#039;&#039;dtostre&#039;&#039; und &#039;&#039;dtostrf&#039;&#039;. dtostre nutzt Exponentialschreibweise (&amp;quot;engineering&amp;quot;-Format). (Hinweis: z.Zt. existiert im avr-gcc kein &amp;quot;echtes&amp;quot; double, intern wird immer mit &amp;quot;einfacher Genauigkeit&amp;quot;, entsprechend float, gerechnet.) &lt;br /&gt;
&lt;br /&gt;
dtostrf und dtostre benötigen die libm.a der avr-libc. Bei Nutzung von Makefiles ist der Parameter -lm in in LDFLAGS anzugeben (Standard in den WinAVR/mfile-Makefilevorlagen). Nutzt man AVRStudio als IDE für den GNU-Compiler (gcc-Plugin) ist die libm.a unter Libaries auszuwählen: Project -&amp;gt; Configurations Options -&amp;gt; Libaries -&amp;gt; libm.a mit dem Pfeil nach rechts einbinden. Siehe auch die [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|FAQ]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
/* lt. avr-libc Dokumentation:&lt;br /&gt;
char* dtostrf(&lt;br /&gt;
  double __val,&lt;br /&gt;
  char   __width,&lt;br /&gt;
  char   __prec,&lt;br /&gt;
  char * __s&lt;br /&gt;
)  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   // Pufferspeicher ausreichend groß&lt;br /&gt;
   // evtl. Vorzeichen + width + Endezeichen:&lt;br /&gt;
   char s[8]; &lt;br /&gt;
   float f = -12.345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   dtostrf( f, 6, 3, s ); &lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // verkürzt: uart_puts( dtostrf( f, 7, 3, s ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfangen ==&lt;br /&gt;
=== einzelne Zeichen empfangen ===&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von Zeichen muss der Empfangsteil des UART bei der Initialisierung aktiviert werden, indem das RXEN-Bit im jeweiligen Konfigurationsregister (UCSRB bzw UCSR0B/UCSR1B) gesetzt wird. Im einfachsten Fall wird solange gewartet, bis ein Zeichen empfangen wurde, dieses steht dann im UART-Datenregister (UDR bzw. UDR0 und UDR1 bei AVRs mit 2 UARTS) zur Verfügung (sogen. &amp;quot;Polling-Betrieb&amp;quot;). Ein Beispiel für den ATmega16:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Zusaetzlich zur Baudrateneinstellung und der weiteren Initialisierung: */&lt;br /&gt;
void Usart_EnableRX()&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= ( 1 &amp;lt;&amp;lt; RXEN );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion blockiert den Programmablauf. Alternativ kann das RXC-Bit in einer Programmschleife abgefragt werden und dann nur bei gesetztem RXC-Bit UDR ausgelesen werden. Eleganter und in den meisten Anwendungsfällen &amp;quot;stabiler&amp;quot; ist die Vorgehensweise, die empfangenen Zeichen in einer Interrupt-Routine einzulesen und zur späteren Verarbeitung in einem Eingangsbuffer (FIFO-Buffer) zwischenzuspeichern. Dazu existieren fertige und gut getestete [[Libraries|Bibliotheken]] &amp;lt;!-- &amp;quot;echte Libraries&amp;quot; (.a) wie im Verweis beschrieben sind hier eigentlich nicht gemeint, verwirrt hier etwas, da AVR-&amp;quot;Libraries&amp;quot; meist per #defines anpassbare Source-Codes sind, vielleicht so: --&amp;gt; und Quellcodekomponenten (z.B. UART-Library von P. Fleury, procyon-avrlib und einige in der &amp;quot;Academy&amp;quot; von avrfreaks.net).&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html Dokumenation der avr-libc/stdlib.h]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_printf Die Nutzung von printf]&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&amp;lt;!-- nimmermehr: * siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (printf etc.) im [[AVR-Tutorial:_UART]]. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: 9bit&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Zeichenketten (Strings) ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Zeichenketten, muß man sich zunächst darüber im klaren sein, daß es ein Kriterium geben muß, an dem der µC erkennen kann, wann ein String zu Ende ist. Sehr oft wird dazu das Zeichen &#039;Return&#039; benutzt, um das Ende eines Strings zu markieren. Dies ist vom Benutzer einfach eingebbar und er ist auch daran gewöhnt, daß er eine Eingabezeile mit einem Druck auf die Return Taste abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Prinzipiell gibt es jedoch keine Einschränkung bezüglich dieses speziellen Zeichens. Es muß nur sichergestellt werden, daß dieses spezielle &#039;Ende eines Strings&#039; - Zeichen nicht mit einem im Text vorkommenden Zeichen verwechselt werden kann. Wenn also im zu übertragenden Text beispielsweise kein &#039;;&#039; vorkommt, dann spricht nichts dagegen, einen String mit einem &#039;;&#039; abschließen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird die durchaus übliche Annahme getroffen, daß eine Stringübertragung identisch ist mit der Übertragung einer Textzeile und daher mit einem Return (&#039;\n&#039;) abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Das Problem der Übertragung eines Strings reduziert sich damit auf die Aufgabenstellung: Empfange und Sammle Zeichen in einem char Array, bis entweder das Array voll ist oder das &#039;String Ende Zeichen&#039; empfangen wurde. Danach wird der empfangene Text noch mit einem &#039;\0&#039; Zeichen abgeschlossen um einen Standard C-String daraus zu machen, mit dem dann weiter gearbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_gets( char* Buffer, uint8_t MaxLen )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t NextChar;&lt;br /&gt;
  uint8_t StringLen = 0;&lt;br /&gt;
&lt;br /&gt;
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen&lt;br /&gt;
&lt;br /&gt;
                                  // Sammle solange Zeichen, bis:&lt;br /&gt;
                                  // * entweder das String Ende Zeichen kam&lt;br /&gt;
                                  // * oder das aufnehmende Array voll ist&lt;br /&gt;
  while( NextChar != &#039;\n&#039; &amp;amp;&amp;amp; StringLen &amp;lt; MaxLen - 1 ) {&lt;br /&gt;
    *Buffer++ = NextChar;&lt;br /&gt;
    StringLen++;&lt;br /&gt;
    NextChar = uart_getc();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
                                  // Noch ein &#039;\0&#039; anhängen um einen Standard&lt;br /&gt;
                                  // C-String daraus zu machen&lt;br /&gt;
  *Buffer = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf ist darauf zu achten, dass das empfangende Array auch mit einer&lt;br /&gt;
vernünftigen Größe definiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
  char Line[40];      // String mit maximal 39 zeichen&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) );&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Interruptbetrieb==&lt;br /&gt;
&lt;br /&gt;
Beim ATMEGA8 ist es möglich, dass RXCIE Bit im Register UCSRB zu setzen.&lt;br /&gt;
Dieser Interrupt wird immer ausglöst, wenn Daten erfolgreich empfangen wurden.&lt;br /&gt;
Zusätzlich braucht man die Routine:&lt;br /&gt;
&lt;br /&gt;
ISR (USART_RXC_vect) {&lt;br /&gt;
   //irgendein Code&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
natürlich muss &amp;quot;Global Interrupt Enable&amp;quot; Aktiviert sein.&lt;br /&gt;
!! Nur getestet beim ATMEGA8 !!&lt;br /&gt;
&lt;br /&gt;
[BAUSTELLE! Aus Lerngründen eventuell als eigenen UART-Interrupt-Block hinter den grundlegenden Interrupt-Teil im Tutorial und hier eine kurze Einführung und einen Verweis darauf anbieten.]&lt;br /&gt;
&lt;br /&gt;
* Unterschied Polling-Betrieb (bisher, oben) und Interrupt-Betrieb&lt;br /&gt;
* Empfangen (Receive)&lt;br /&gt;
** Verändertes UART-Init, ISR (RXC), ggf. Fallstricke ([http://www.mikrocontroller.net/topic/84256#707214 UDR in der ISR lesen!]), Philosophie einer ISR (kurz und schmerzlos), Datenaustausch ISR zu Restprogramm (volatile)&lt;br /&gt;
** Einfachstbeispiel ([http://www.mikrocontroller.net/topic/84228#707052 Echo] (noch buggy beim Datenzugriff, siehe Lit. 2+3!)), ggf. LED zur ISR-Empfangsanzeige oder Overflow-Anzeige&lt;br /&gt;
* FIFO-Puffer, Ringpuffer&lt;br /&gt;
* Senden (Transmit)&lt;br /&gt;
** Variante &amp;quot;UART Data Register Empty&amp;quot; (UDRE) &lt;br /&gt;
** Variante &amp;quot;UART Transmit Complete&amp;quot; (TXC) &lt;br /&gt;
* Fertige UART-Bibliotheken (-&amp;gt;Fleury, -&amp;gt;Procyon)&lt;br /&gt;
* Literatur: &lt;br /&gt;
** 1/ [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=48188 avrfreaks.net Tutorial] inkl. Diskussion (engl.)&lt;br /&gt;
** 2/ avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_16bitio Why do some 16-bit timer registers sometimes get trashed?]&lt;br /&gt;
** 3/ [[Interrupt]] und atomarer Datenzugriff&lt;br /&gt;
&lt;br /&gt;
==Software-UART==&lt;br /&gt;
&lt;br /&gt;
Falls die Zahl der vorhandenen Hardware-UARTs nicht ausreicht, können weitere Schnittstellen über sogennante Software-UARTs ergänzt werden. Es gibt dazu (mindestens) zwei Ansätze: &lt;br /&gt;
* Der bei AVRs üblichste Ansatz basiert auf dem Prinzip, dass ein externer Interrupt-Pin für den Empfang (&amp;quot;RX&amp;quot;) genutzt wird. Das Startbit löst den Interrupt aus, in der Interrupt-Routine (ISR) wird der externe Interrupt deaktiviert und ein Timer aktiviert. In der Interrupt-Routine des Timers wird der Zustand des Empfangs-Pins entsprechend der Baudrate abgetastet. Nach Empfang des Stop-Bits wird der externe Interrupt wieder aktiviert. Senden kann über einen beliebigen Pin (&amp;quot;TX&amp;quot;) erfolgen, der entsprechend der Baudrate und dem zu sendenden Zeichen auf 0 oder 1 gesetzt wird. Die Implementierung ist nicht ganz einfach, es existieren dazu aber fertige Bibliotheken (z.B. bei [http://www.avrfreaks.net/ avrfreaks] oder in der [http://hubbard.engr.scu.edu/embedded/avr/avrlib/ Procyon avrlib]).&lt;br /&gt;
* Ein weiterer Ansatz erfordert keinen Pin mit &amp;quot;Interrupt-Funktion&amp;quot; aber benötigt mehr Rechenzeit. Jeder Input-Pin kann als Empfangspin (RX) dienen. Über einen Timer wird der Zustand des RX-Pins mit einem vielfachen der Baudrate abgetastet (dreifach scheint üblich) und High- bzw. Lowbits anhand einer Mindestanzahl identifiziert. (Beispiel: &amp;quot;Generic Software Uart&amp;quot; Application-Note von IAR)&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (z.B. ATtiny26 oder ATmega48,88,168,169) verfügen über ein Universal Serial Interface (USI), das teilweise UART-Funktion übernehmen kann. Atmel stellt eine Application-Note bereit, in der die Nutzung des USI als UART erläutert wird (im Prinzip &amp;quot;Hardware-unterstützter Software-UART&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==Handshaking==&lt;br /&gt;
Wenn der Sender ständig sendet, wird irgendwann der Fall eintreten, daß der Empfänger nicht bereit ist, neue Zeichen zu empfangen. In diesem Fall muß durch ein &#039;&#039;&#039;Handshake-Verfahren&#039;&#039;&#039; die Situation bereinigt werden. Handshake bedeutet nichts anderes, als daß der Empfänger dem Sender mitteilt, daß er zur Zeit keine Daten annehmen kann und der Sender die Übertragung der nächsten Zeichen solange einstellen soll, bis der Empfänger signalisiert, daß er wieder Zeichen aufnehmen kann.&lt;br /&gt;
===Hardwarehandshake (RTS/CTS)===&lt;br /&gt;
Beim Hardwarehandshake werden zusätzlich zu den beiden Daten-Übertragungsleitungen noch 2 weitere Leitungen benötigt: &#039;&#039;&#039;RTS&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;equest &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end) und &#039;&#039;&#039;CTS&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end). Jeder der beiden Kommunikationspartner ist verpflichtet, bevor ein Zeichen gesendet wird, den Zustand der &#039;&#039;&#039;RTS&#039;&#039;&#039; Leitung zu überprüfen. Nur wenn die Gegenstelle darauf Empfangsbereitschaft signalisiert, darf das Zeichen gesendet werden. Um der Gegenstelle zu signalisieren, daß sie zur Zeit keine Zeichen schicken soll, wird die Leitung &#039;&#039;&#039;CTS&#039;&#039;&#039; benutzt.&lt;br /&gt;
&lt;br /&gt;
===Softwarehandshake (XON/XOFF)===&lt;br /&gt;
Beim Softwarehandshake sind keine speziellen Leitungen notwendig. Statt dessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, daß Senden einzustellen bzw. wieder aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;XOFF&#039;&#039;&#039; Aufforderung das Senden einzustellen&lt;br /&gt;
* &#039;&#039;&#039;XON&#039;&#039;&#039;  Gegenstelle darf wieder senden&lt;br /&gt;
&lt;br /&gt;
Nachteilig bei einem Softwarehandshake ist es, daß dadurch keine direkte binäre Datenübertragung mehr möglich ist. Von den möglichen 256 Bytewerten werden ja 2 (nämlich &#039;&#039;&#039;XON&#039;&#039;&#039; und &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus.&lt;br /&gt;
&lt;br /&gt;
==Fehlersuche==&lt;br /&gt;
Erstaunlich oft wird im Forum der Hilferuf laut: &amp;quot;Meine UART funktioniert nicht, was mache ich falsch&amp;quot;. In der überwiegenden Mehrzahl der Fälle stellt sich dann heraus, daß es sich um ein Hardwareproblem handelt, wobei da wiederrum der Löwenanteil auf das Konto einer nicht korrekt eingestellten Taktrate geht: Der µC benutzt nicht einen angeschlossenen Quarz, so wie er auch im Programm eingetragen ist, sondern läuft immer noch mit dem internen RC-Takt. Daraus resultiert aber auch, daß der Baudraten Konfigurationswert falsch berechnet wird.&lt;br /&gt;
&lt;br /&gt;
Eine Checkliste zum Aufspüren solcher Fehler findet sich [[AVR_Checkliste#UART.2FUSART|hier]].&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Analoge Eingangswerte werden in der Regel über den AVR Analog-Digital-Converter (AD-Wandler, ADC) eingelesen, der in vielen Typen verfügbar ist (typisch 10bit Auflösung). Durch diesen werden analogen Signale (Spannungen) in digitale Zahlenwerte gewandelt. Bei AVRs, die über keinen internen AD-Wandler verfügen (z.B. ATmega162, ATtiny2313), kann durch externe Beschaltung (R/C-Netzwerk und &amp;quot;Zeitmessung&amp;quot;) die Funktion des AD-Wandlers &amp;quot;emuliert&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
Es existieren keine AVRs mit eingebautem Digital-Analog-Konverter (DAC). Diese Funktion muss durch externe Komponenten nachgebildet werden (z.B. PWM und &amp;quot;Glättung&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Unabhängig davon besteht natürlich immer die Möglichkeit, spezielle Bausteine zur Analog-Digital- bzw. Digital-Analog-Wandlung zu nutzen und diese über eine digitale Schnittstelle (z.b. SPI oder I2C) mit einem AVR anzusteuern.&lt;br /&gt;
&lt;br /&gt;
== AC (Analog Comparator) ==&lt;br /&gt;
&lt;br /&gt;
Der Comparator vergleicht 2 Spannungen an den Pins AIN0 und AIN1 und gibt einen Status aus welche der beiden Spannungen größer ist. AIN0 Dient dabei als Referenzspannung (Sollwert) und AIN1 als Vergleichsspannung (Istwert). Als Referenzspannung kann auch alternativ eine interne Referenzspannung ausgewählt werden.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Liegt die Vergleichsspannung (IST) unter der der Referenzspannung (SOLL) gibt der Comperator eine logische 0 aus. ISt die Vergleichsspannung hingegen größer als die Referenzspannung wird eine logische 1 ausgegeben.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Der Comperator arbeitet völlig autark bzw. parallel zum Prozessor. Für mobile Anwendungen empfiehlt es sich ihn abzuschalten sofern er nicht benötigt wird, da er ansonsten Strom benötigt. Der Comparator kann Interruptgesteuert abgefragt werden oder im Pollingbetrieb.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Steuer- bzw. Statusregister ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ACSR (0x28) - Analog Comparator Status Register&amp;lt;BR&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACD&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;AINBG&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACO&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACI&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | n/a&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 7 ACD - Analog Comperator Disable: 0 = Comparator ein, 1 = Comparator aus. Wird dieses Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 ACIE ggf. abgeschaltet werden.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 6 AINBG - Analog Comparator Bandgap Select: Ermöglicht das umschalten zwischen interner und externer Referenzspannung. 1 = interne (~1,3 Volt), 0 = externe Referenzspannung (an Pin AIN0)&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 5 ACO - Analog Coparator Output: Hier wird das Ergebnis des Vergleichs angezeigt. Es liegt typischerweise nach 1-2 Taktzyklen vor. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ergebnis: &amp;lt;BR&amp;gt;IST &amp;lt; SOLL --&amp;gt; 0&amp;lt;BR&amp;gt;&lt;br /&gt;
IST &amp;gt; SOLL --&amp;gt; 1&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4 ACI - Analog Comparator Interrupt Flag: Dieses Bit wird von der Hardware gesetzt wenn ein Interruptereignis das in Bit 0 und 1 definiert ist eintritt. Dieses Bit löst noch keinen Interrupt aus! Die Interruptroutine wird nur dann ausgeführt wenn das Bit 3 ACIE gesetzt ist und global Interrupts erlaubt sind (I-Bit in SREG=1). Das Bit 4 ACI wird wieder gelöscht wenn die Interruptroutine ausgeführt wurde oder wenn manuell das Bit auf 1 gesetzt wird. Das Bit kann für Abfragen genutzt werden, steuert oder konfuguriert aber nicht den Comparator.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 3 ACIE - Analog Comparator Interrupt Enable: Ist das Bit auf 1 gesetzt wird immer ein Interrupt ausgelöst wenn das Ereignis das in Bit 1 und 0 definiert ist eintritt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 2 ACIC - Analog Comparator Input Capture Enable: Wird das Bit gesetzt wird der Comparatorausgang intern mit dem Counter 1 verbunden. Es könnten damit z.b. die Anzahl der Vergleiche im Counter1 gezählt werden. Um den Comparator an den Timer1 Input Capture Interrupt zu verbinden muß im Timerregister das TICIE1 Bit auf 1 gesetzt werden. Der Trigger wird immer dann ausgelöst wenn das in Bit 1 und 0 definierte Ereignis eintritt.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 1,0 ACIS1,ACIS0 - Analog Comparator Interrupt select: Hier wird definiert welche Ereignisse einen Interrupt auslösen sollen:&amp;lt;BR&amp;gt;&lt;br /&gt;
00 = Interrupt auslösen bei jedem Flankenwechsel&amp;lt;BR&amp;gt;&lt;br /&gt;
10 = Interrupt auslösen bei fallender Flanke&amp;lt;BR&amp;gt;&lt;br /&gt;
11 = Interrupt auslösen bei steigender Flanke&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Werden diese Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 gelöscht werden.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der Analog-Digital-Konverter (ADC) wandelt analoge Signale in digitale Werte um, welche vom Controller interpretiert werden können. Einige AVR-Typen haben bereits einen mehrkanaligen Analog-Digital-Konverter eingebaut. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst werden kann, wird durch die Auflösung des ADC in Anzahl Bits angegeben, man hört bzw. liest jeweils von 8-Bit-ADC oder 10-Bit-ADC oder noch höher. ADCs die in AVRs enthalten sind haben zur Zeit eine maximale Auflösung von 10-Bit.&lt;br /&gt;
&lt;br /&gt;
Ein ADC mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des Analog-Digital-Konverters ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Der interne ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem Analog-Digital-Wandler (ADC) zurückgreifen, die über mehrere Kanäle verfügen. Kanäle heißt in diesem Zusammenhang, dass zwar bis zu zehn analoge Eingänge am AVR verfügbar sind, aber nur ein &amp;quot;echter&amp;quot; Analog-Digital-Wandler zur Verfügung steht, vor der eigentlichen Messung ist also einzustellen, welcher Kanal (&amp;quot;Pin&amp;quot;) mit dem Wandler verbunden und gemessen wird.&lt;br /&gt;
&lt;br /&gt;
Die Umwandlung innerhalb des AVR basiert auf der schrittweisen Näherung. Beim AVR müssen die Pins &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AVCC&#039;&#039;&#039; beschaltet werden. Für genaue Messungen sollte AVCC über ein L-C Netzwerk mit VCC verbunden werden, um Spannungsspitzen und -einbrüche vom Analog-Digital-Wandler fernzuhalten. Im Datenblatt findet sich dazu eine Schaltung, die 10uH und 100nF vorsieht.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Analog-Digital-Wandlung wird auf eine Referenzspannung bezogen. Aktuelle AVRs bieten drei Möglichkeiten zur Wahl dieser Spannung:&lt;br /&gt;
&lt;br /&gt;
* Eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; am Anschlusspin &#039;&#039;&#039;AREF&#039;&#039;&#039;. Die minimale (externe) Referenzspannung darf jedoch nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. &lt;br /&gt;
&lt;br /&gt;
* Verfügt der AVR über eine interne Referenzspannung, kann diese genutzt werden. Alle aktuellen AVRs mit internem AD-Wandler sollten damit ausgestattet sein (vgl. Datenblatt: 2,56V oder 1,1V je nach Typ). Das Datenblatt gibt auch über die Genauigkeit dieser Spannung Auskunft.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;interne&#039;&#039;&#039; Referenzspannung wird auf &#039;&#039;&#039;Vcc&#039;&#039;&#039; bezogen, eine externe Referenzspannung auf &#039;&#039;&#039;GND (Masse)&#039;&#039;&#039;. Davon unabhängig werden digitalisierte Spannungen &#039;&#039;&#039;immer&#039;&#039;&#039; auf GND bezogen. Beim ATMEGA8 z.B. ist der Pin AREF über 32kOhm mit GND verbunden, d.h. man muss diese doch extrem niedrige Eingangsimpedanz mit in die Berechnung für einen Spannungsteiler einbeziehen, bzw. kann diesen Widerstand als R2 gleich mit benutzen. Formel für Spannungsteiler: Udiv = U / ((R1 + R2) / R2)&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung von Vcc oder der internen Referenz wird empfohlen, einen Kondensator zwischen dem AREF-Pin und GND anzuordnen. Die Festlegung, welche Spannungsreferenz genutzt wird, erfolgt z.B. beim ATmega16 mit den Bits REFS1/REFS0 im ADMUX-Register. Die zu messende Spannung muss im Bereich zwischen &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AREF&#039;&#039;&#039; (egal ob intern oder extern) liegen. &lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; kann in zwei verschiedenen Betriebsarten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
; Einfache Wandlung (Single Conversion) : In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestoßen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
; Frei laufend (Free Running) : In dieser Betriebsart erfasst der Wandler permanent die anliegende Spannung und schreibt diese in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Die Register des ADC ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; verfügt über eigene Register. Im Folgenden die Registerbeschreibung eines  ATMega16, welcher über 8 ADC-Kanäle verfügt. Die Register unterscheiden sich jedoch nicht erheblich von denen anderer AVRs (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister A.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den &#039;&#039;&#039;ADC&#039;&#039;&#039; verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADEN&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able)&lt;br /&gt;
:Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren. Wenn das Bit nicht gesetzt ist, können die Pins wie normale I/O-Pins verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADSC&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion)&lt;br /&gt;
:Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.&lt;br /&gt;
:Wenn das Bit nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.&lt;br /&gt;
:Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADFR&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;F&#039;&#039;&#039;ree &#039;&#039;&#039;R&#039;&#039;&#039;un select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Ist das Bit auf 1 gesetzt arbeitet der ADC im Freerunning Modus. Dabe iwird das Datenregister permanent aktualisiert. Ist das Bit hingegen auf 0 gesetzt macht der ADC nur eine Single Conversion. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIF&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag)&lt;br /&gt;
:Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt, sobald eine Umwandlung erfolgt ist und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert wurde. Das Bit wird bei lesendem Zugriff auf &#039;&#039;&#039;ADC(L,H)&#039;&#039;&#039; automatisch (d.h. durch die Hardware) gelöscht.&lt;br /&gt;
:Wenn das &#039;&#039;&#039;ADIE&#039;&#039;&#039; Bit sowie das &#039;&#039;&#039;I-Bit&#039;&#039;&#039; im AVR &#039;&#039;&#039;Statusregister&#039;&#039;&#039; gesetzt ist, wird der &#039;&#039;&#039;ADC Interrupt&#039;&#039;&#039; ausgelöst und die Interrupt-Behandlungsroutine aufgerufen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn die Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 in das Register geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIE&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;, dann wird der &#039;&#039;&#039; ADC-Interrupt&#039;&#039;&#039; aktiviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADPS2...ADPS0&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;P&#039;&#039;&#039;rescaler &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
:Der &#039;&#039;&#039;ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz ergibt.&lt;br /&gt;
:Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
TF_{min}=\frac{CLK}{200\,\mathrm{kHz}}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50\,\mathrm{kHz}}=\frac{4000000}{50000}=\mathbf{80}&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im Interesse der schnelleren Wandlungszeit werden wir hier den Faktor 32 einstellen.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCL&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ADCH&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC &#039;&#039;&#039; Data Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn eine Umwandlung abgeschlossen ist, befindet sich der gemessene Wert in&lt;br /&gt;
diesen beiden Registern. Von &#039;&#039;&#039;ADCH&#039;&#039;&#039; werden nur die beiden niederwertigsten Bits verwendet. Es müssen immer beide Register ausgelesen werden, und zwar immer &#039;&#039;&#039;in der Reihenfolge: ADCL, ADCH&#039;&#039;&#039;. &lt;br /&gt;
Der effektive Messwert ergibt sich dann zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCL;       // mit uint16_t x&lt;br /&gt;
x += (ADCH&amp;lt;&amp;lt;8); // in zwei Zeilen (LSB/MSB-Reihenfolge und&lt;br /&gt;
                // C-Operatorpriorität sichergestellt)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADMUX&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Mu&#039;&#039;&#039;ltiple&#039;&#039;&#039;x&#039;&#039;&#039;er Select Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Register wird der zu messende Kanal ausgewählt. Beim 90S8535&lt;br /&gt;
kann jeder Pin von Port A als &#039;&#039;&#039;ADC&#039;&#039;&#039;-Eingang verwendet werden (=8 Kanäle).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADLAR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX4&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX3&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;REFS1...REFS0&#039;&#039;&#039; (&#039;&#039;&#039;Ref&#039;&#039;&#039;erence&#039;&#039;&#039;S&#039;&#039;&#039;election Bits)&lt;br /&gt;
:Mit diesen Bits kann die Referenzspannung eingestellt werden:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Referenzspanung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Externes AREF&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | AVCC als Referenz&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Interne 2,56 Volt&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADLAR&#039;&#039;&#039; (&#039;&#039;&#039;ADC &#039;&#039;&#039; &#039;&#039;&#039;L&#039;&#039;&#039;eft &#039;&#039;&#039;A&#039;&#039;&#039;djust &#039;&#039;&#039;R&#039;&#039;&#039;esult)&lt;br /&gt;
:Das ADLAR Bit verändert das Aussehen des Ergebnisses der AD-Wandlung. Bei einer logischen 1 wird das Ergebnis linksbündig ausgegeben, bei einer 0 rechtsündig. Eine Änderung in diesem Bit beeinflusst das Ergebnis sofort, ganz egal ob bereits eine Wandlung läuft.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX4...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 5 Bits wird der zu messende Kanal bestimmt. Wenn man einen einfachen 1-kanaligen ADC verwendet wird einfach die entsprechende Pinnummer des Ports in die Bits 0...2 eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
:Eine Empfehlung ist deswegen diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren des ADC ====&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039; ADC&#039;&#039;&#039; zu aktivieren, müssen wir das &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bit im &#039;&#039;&#039;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
Ein kleines Beispiel für den &amp;quot;single conversion&amp;quot;-Mode bei einem ATmega169 und Nutzung der internen Referenzspannung (beim &#039;169 1,1V bei anderen AVRs auch 2,56V). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler einstellen. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1023 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint16_t ReadChannel(uint8_t mux)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
&lt;br /&gt;
  ADMUX = mux;                      // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler &lt;br /&gt;
                               // setzen auf 8 (1) und ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  /* nach Aktivieren des ADC wird ein &amp;quot;Dummy-Readout&amp;quot; empfohlen, man liest&lt;br /&gt;
     also einen Wert und verwirft diesen, um den ADC &amp;quot;warmlaufen zu lassen&amp;quot; */&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
     ;     // auf Abschluss der Konvertierung warten &lt;br /&gt;
  }&lt;br /&gt;
  result = ADCW;  // ADCW muss einmal gelesen werden,&lt;br /&gt;
                  // sonst wird Ergebnis der nächsten Wandlung&lt;br /&gt;
                  // nicht übernommen.&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  result = 0; &lt;br /&gt;
  for( i=0; i&amp;lt;4; i++ )&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);            // eine Wandlung &amp;quot;single conversion&amp;quot;&lt;br /&gt;
    while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
      ;   // auf Abschluss der Konvertierung warten&lt;br /&gt;
    }&lt;br /&gt;
    result += ADCW;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Beispielaufrufe: */&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
&lt;br /&gt;
  adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -&amp;gt; Channel 0 */&lt;br /&gt;
  ...&lt;br /&gt;
  adcval = ReadChannel(2); /* MUX-Bits auf 0b0010 -&amp;gt; Channel 2 */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekennzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Das Löschen des ADIF-Flags sollte, &#039;&#039;&#039;entgegen&#039;&#039;&#039; der [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_intbits FAQ], mit&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADIF);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
erfolgen. Die Methode in der FAQ eignet sich nur für Register in denen &#039;&#039;&#039;nur&#039;&#039;&#039; Interrupt-Flags stehen.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Analog-Digital-Wandlung ohne internen ADC ===&lt;br /&gt;
&lt;br /&gt;
==== Messen eines Widerstandes ====&lt;br /&gt;
&lt;br /&gt;
Analoge Werte lassen sich ohne Analog-Digital-Wandler auch indirekt ermitteln. Im Folgenden wird die Messung des an einem Potentiometer eingestellten Widerstands anhand der Ladekurve eines Kondensators erläutert. Bei dieser Methode wird nur ein Portpin benötigt, ein Analog-Digital-Wandler oder Analog-Comparator ist nicht erforderlich. Es wird dazu ein Kondensator und der Widerstand (das Potentiometer) in Reihe zwischen Vorsorgungsspannung und Masse/GND geschaltet (sogen. RC-Netzwerk). Zusätzlich wird eine Verbindung der Leitung zwischen Kondensator und Potentiometer zu einem Portpin des Controllers hergestellt. Die folgende Abbildung verdeutlicht die erforderliche Schaltung. &lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wird der Portpin des Controllers auf Ausgang konfiguriert (im Beispiel &#039;&#039;DDRD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) und dieser Ausgang auf Logisch 1 (&amp;quot;High&amp;quot;, &#039;&#039;PORTD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) geschaltet, liegt an beiden &amp;quot;Platten&amp;quot; des Kondensators das gleiche Potential &#039;&#039;&#039;VCC&#039;&#039;&#039; an und der Kondensator somit entladen. (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so, da an beiden Seiten des Kondensators das gleiche Potential anliegt und somit eine Potentialdifferenz von 0V besteht =&amp;gt; Kondensator ist entladen).&lt;br /&gt;
&lt;br /&gt;
Nach einer gewissen Zeit ist der Kondensator entladen und der Portpin wird als Eingang konfiguriert (&#039;&#039;DDRD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2); PORTD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2)&#039;&#039;), wodurch dieser hochohmig wird. Der Status des Eingangspin (in PIND) ist Logisch 1 (High). Der Kondensator lädt sich jetzt über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), wird das Eingangssignal als LOW erkannt (Bit in PIND wird 0). Die Zeitspanne zwischen der Umschaltung von Entladung auf Aufladung und dem Wechsel des Eingangssignals von High auf Low ist ein Maß für den am Potentiometer eingestellten Widerstand. Zur Zeitmessung kann einer der im Controller vorhandenen Timer genutzt werden. Der 220 Ohm Widerstand dient dem Schutz des Controllers. Es würde sonst bei Maximaleinstellung des Potentionmeters (hier 0 Ohm) ein zu hoher Strom fließen, der die Ausgangsstufe des Controllers zerstört. &lt;br /&gt;
&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B: 0...100 % oder so) umzurechnen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Link 404 =&amp;gt; auskommentiert, mthomas 9.2.2008 &lt;br /&gt;
Ein Beispielprogramm findet sich auf [http://www.mypage.bluewin.ch/ch_schifferle/ Christian Schifferles Web-Seite] im Archiv &#039;&#039;ATMEL.ZIP&#039;&#039;, welches unter den Titel &#039;&#039;Tutorial &amp;quot;Programmieren mit C für Atmel Mikrocontroller&#039;&#039; heruntergeladen werden kann. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ADC über Komparator ====&lt;br /&gt;
&lt;br /&gt;
Es gibt einen weiteren Weg, eine analoge Spannung mit Hilfe des&lt;br /&gt;
Komparators, welcher in fast jedem AVR integriert ist, zu messen. Siehe dazu&lt;br /&gt;
auch die Application Note AVR400 von Atmel.&lt;br /&gt;
&lt;br /&gt;
Dabei wird das zu messende Signal auf den invertierenden Eingang&lt;br /&gt;
des Komparators geführt. Zusätzlich wird ein Referenzsignal an den nicht&lt;br /&gt;
invertierenden Eingang des Komparators angeschlossen. Das Referenzsignal wird&lt;br /&gt;
hier auch wieder über ein RC-Glied erzeugt, allerdings mit festen Werten für R&lt;br /&gt;
und C.&lt;br /&gt;
&lt;br /&gt;
[[Image:ADC ueber Komparator.gif]]&lt;br /&gt;
&lt;br /&gt;
Das Prinzip der Messung ist nun dem vorhergehenden recht&lt;br /&gt;
ähnlich. Durch Anlegen eines LOW-Pegels an Pin 2 wird der Kondensator zuerst&lt;br /&gt;
einmal entladen. Auch hier muss darauf geachtet werden, dass der Entladevorgang&lt;br /&gt;
genügend lang dauert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Nun wird Pin 2 auf HIGH gelegt. Der Kondensator wird geladen. Wenn die Spannung&lt;br /&gt;
über dem Kondensator die am Eingangspin anliegende Spannung erreicht hat&lt;br /&gt;
schaltet der Komparator durch. Die Zeit, welche benötigt wird, um den&lt;br /&gt;
Kondensator zu laden kann nun auch wieder als Maß für die Spannung an Pin 1&lt;br /&gt;
herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Ich habe es mir gespart, diese Schaltung auch aufzubauen und&lt;br /&gt;
zwar aus mehreren Gründen:&lt;br /&gt;
&lt;br /&gt;
# 3 Pins notwendig.&lt;br /&gt;
# Genauigkeit vergleichbar mit einfacherer Lösung.&lt;br /&gt;
# War einfach zu faul.&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung liegt allerdings darin, dass damit&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines Digital-Analog-Konverters (&#039;&#039;&#039;DAC&#039;&#039;&#039;) können wir nun auch Analogsignale ausgeben. Es gibt hier mehrere Verfahren. &amp;lt;!-- Wenn wir beim ADC die Möglichkeit haben, mit externen Komponenten zu operieren, müssen wir bei der DAC-Wandlung mit dem auskommen, was der Controller selber zu bieten hat. --mt: hmm, richtig? verstaendlich? redundant? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DAC über mehrere digitale Ausgänge ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir an den Ausgängen des Controllers ein entsprechendes&lt;br /&gt;
Widerstandsnetzwerk aufbauen haben wir die Möglichkeit, durch die Ansteuerung&lt;br /&gt;
der Ausgänge über den Widerständen einen Addierer aufzubauen, mit dessen&lt;br /&gt;
Hilfe wir eine dem Zahlenwert proportionale Spannung erzeugen können. Das&lt;br /&gt;
Schaltbild dazu kann etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
[[Image:DAC R2R.gif]]&lt;br /&gt;
&lt;br /&gt;
Es sollten selbstverständlich möglichst genaue Widerstände verwendet&lt;br /&gt;
werden, also nicht unbedingt solche mit einer Toleranz von 10% oder mehr.&lt;br /&gt;
Weiterhin empfiehlt es sich, je nach Anwendung den Ausgangsstrom über einen&lt;br /&gt;
Operationsverstärker zu verstärken.&lt;br /&gt;
&lt;br /&gt;
=== PWM (Pulsweitenmodulation) ===&lt;br /&gt;
&lt;br /&gt;
Wir kommen nun zu einem Thema, welches in aller Munde ist, aber viele&lt;br /&gt;
Anwender verstehen nicht ganz, wie &#039;&#039;&#039; PWM&#039;&#039;&#039; eigentlich funktioniert.&lt;br /&gt;
&lt;br /&gt;
Wie wir alle wissen, ist ein Mikrocontroller ein rein digitales Bauteil.&lt;br /&gt;
Definieren wir einen Pin als Ausgang, dann können wir diesen Ausgang entweder&lt;br /&gt;
auf HIGH setzen, worauf am Ausgang die Versorgungsspannung &#039;&#039;&#039; Vcc&#039;&#039;&#039; anliegt, oder aber wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt. Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen HIGH und LOW umschalten? - Richtig, wir erhalten eine Rechteckspannung, wie die folgende Abbildung zeigt:&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 1.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Rechteckspannung hat nun einen arithmetischen Mittelwert, &lt;br /&gt;
der je nach Pulsbreite kleiner oder größer ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 2.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun diese pulsierende Ausgangsspannung noch über ein RC-Glied filtern/&amp;quot;glätten&amp;quot;, dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVRs können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. &lt;br /&gt;
Dazu dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt, weshalb ein Blick ins entsprechende Datenblatt dringend angeraten wird.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren, müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| PWM-Modus des Timers ist nicht aktiv.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Timer/Counter zählt nun permanent von 0 bis zur Obergrenze&lt;br /&gt;
und wieder zurück, er wird also als sogenannter Auf-/Ab Zähler betrieben. &lt;br /&gt;
Die Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss mit den Bits &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; desselben&lt;br /&gt;
Registers die gewünschte Ausgabeart des Signals definiert werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Nicht invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Hochzählen und gesetzt beim&lt;br /&gt;
Herunterzählen.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Herunterzählen und gesetzt beim&lt;br /&gt;
Hochzählen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl, um beispielsweise den Timer/Counter als&lt;br /&gt;
nicht invertierenden 10-Bit PWM zu verwenden, heißt dann:&lt;br /&gt;
&lt;br /&gt;
alte Schreibweise (PWMxx wird nicht mehr akzeptiert)&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;PWM11)|(1&amp;lt;&amp;lt;PWM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
neue Schreibweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;WGM11)|(1&amp;lt;&amp;lt;WGM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Timer/Counter überhaupt läuft, müssen wir im Control&lt;br /&gt;
Register B &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; noch den gewünschten Takt (Vorteiler) einstellen und&lt;br /&gt;
somit auch die Frequenz des &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signals bestimmen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stop. Der Timer/Counter wird gestoppt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin 1, negative Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin 1, positive Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Also um einen Takt von CK / 1024 zu generieren, verwenden wir&lt;br /&gt;
folgenden Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1B = (1&amp;lt;&amp;lt;CS12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt muss nur noch der Vergleichswert festgelegt werden. Diesen&lt;br /&gt;
schreiben wir in das 16-Bit Timer/Counter Output Compare Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem Vergleichswert und dem generierten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal aufzeigen.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
Ach ja, fast hätte ich&#039;s vergessen. Das generierte &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal&lt;br /&gt;
wird am Output Compare Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; des Timers ausgegeben und leider können wir&lt;br /&gt;
deshalb auch beim AT90S2313 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren. Andere AVR-Typen verfügen über bis zu vier PWM-Ausgänge. Zu beachten ist außerdem, das wenn der OC Pin aktiviert ist, er nichtmehr wie üblich funktioniert und z.B. nicht einfach über PINx ausgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Ein Programm, dass an einem ATMega8 den Fast-PWM Modus verwendet, den Modus 14, könnte so aussehen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // OC1A auf Ausgang&lt;br /&gt;
  DDRB = (1 &amp;lt;&amp;lt; PB1 );&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // Timer 1 einstellen&lt;br /&gt;
  //  &lt;br /&gt;
  // Modus 14:&lt;br /&gt;
  //    Fast PWM, Top von ICR1&lt;br /&gt;
  //&lt;br /&gt;
  //     WGM13    WGM12   WGM11    WGM10&lt;br /&gt;
  //      1        1       1        0&lt;br /&gt;
  //&lt;br /&gt;
  //    Timer Vorteiler: 1&lt;br /&gt;
  //     CS12     CS11    CS10&lt;br /&gt;
  //       0        0       1&lt;br /&gt;
  //&lt;br /&gt;
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match&lt;br /&gt;
  //     COM1A1   COM1A0&lt;br /&gt;
  //       1        0&lt;br /&gt;
 &lt;br /&gt;
  TCCR1A = (1&amp;lt;&amp;lt;COM1A1) | (1&amp;lt;&amp;lt;WGM11);&lt;br /&gt;
  TCCR1B = (1&amp;lt;&amp;lt;WGM13) | (1&amp;lt;&amp;lt;WGM12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  //  den Endwert (TOP) für den Zähler setzen&lt;br /&gt;
  //  der Zähler zählt bis zu diesem Wert&lt;br /&gt;
&lt;br /&gt;
  ICR1 = 0x6FFF;&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  // der Compare Wert&lt;br /&gt;
  // Wenn der Zähler diesen Wert erreicht, wird mit&lt;br /&gt;
  // obiger Konfiguration der OC1A Ausgang abgeschaltet&lt;br /&gt;
  // Sobald der Zähler wieder bei 0 startet, wird der&lt;br /&gt;
  // Ausgang wieder auf 1 gesetzt&lt;br /&gt;
  //&lt;br /&gt;
  // Durch Verändern dieses Wertes, werden die unterschiedlichen&lt;br /&gt;
  // PWM Werte eingestellt.&lt;br /&gt;
&lt;br /&gt;
  OCR1A = 0x3FFF;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  while( 1 )&lt;br /&gt;
    ;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für Details der PWM Möglichkeiten, muß immer das jeweilge Datenblatt des Prozessors konsultiert werden, da sich die unterschiedlichen Prozessoren in ihren Möglichkeiten doch stark unterscheiden. Auch muß man aufpassen, welches zu setzende Bit in welchem Register sind. Auch hier kann es sein, dass gleichnamige Konfigurationsbits in unterschiedlichen Konfigurationsregistern (je nach konkretem Prozessortyp) sitzen.&lt;br /&gt;
&lt;br /&gt;
= LCD-Ansteuerung =&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung an der LCD-Controller-Platine ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; (ist in manchen Unterlagen auch &#039;&#039;&#039;EN&#039;&#039;&#039;  für &#039;&#039;Enable&#039;&#039; abgekürzt) benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert, ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitetet hat (diese Methode u.a. in der LCD-Library von Peter Fleury verwendet). Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt da wir wissen, welche Anschlüsse das LCDs benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin #-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin-µC&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND oder Poti (siehe oben)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD4 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD5 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD0 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD1 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD2 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD3 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man die Steuerleitungen EN und RS auf Pins an einem anderen Port legen möchte, kann man so wie in diesem [http://www.mikrocontroller.net/topic/88543#751982 Forumsbeitrag] vorgehen.&lt;br /&gt;
&lt;br /&gt;
Ok, alles ist verbunden, wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display? &lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
void lcd_data(unsigned char temp1);&lt;br /&gt;
void lcd_string(char *data);&lt;br /&gt;
void lcd_command(unsigned char temp1);&lt;br /&gt;
void lcd_enable(void);&lt;br /&gt;
void lcd_init(void);&lt;br /&gt;
void lcd_home(void);&lt;br /&gt;
void lcd_clear(void);&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y);&lt;br /&gt;
&lt;br /&gt;
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
&lt;br /&gt;
// LCD Befehle&lt;br /&gt;
&lt;br /&gt;
#define CLEAR_DISPLAY 0x01&lt;br /&gt;
#define CURSOR_HOME   0x02&lt;br /&gt;
&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
&lt;br /&gt;
#define LCD_PORT      PORTD&lt;br /&gt;
#define LCD_DDR       DDRD&lt;br /&gt;
#define LCD_RS        PD4&lt;br /&gt;
#define LCD_EN        PD5&lt;br /&gt;
// DB4 bis DB7 des LCD sind mit PD0 bis PD3 des AVR verbunden&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// sendet ein Datenbyte an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_data(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_RS);        // RS auf 1 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_command(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);        // RS auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;              // oberes Nibble holen&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;            // maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;            // unteres Nibble holen und maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// erzeugt den Enable-Puls&lt;br /&gt;
void lcd_enable(void)&lt;br /&gt;
{&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
    _delay_us(1);                   // kurze Pause&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/80900&lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Initialisierung: &lt;br /&gt;
// Muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
void lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
   LCD_DDR = LCD_DDR | 0x0F | (1&amp;lt;&amp;lt;LCD_RS) | (1&amp;lt;&amp;lt;LCD_EN);   // Port auf Ausgang schalten&lt;br /&gt;
&lt;br /&gt;
   // muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(15);&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x03;            &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);      // RS auf 0&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4 Bit Modus aktivieren &lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x02;&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4Bit / 2 Zeilen / 5x7&lt;br /&gt;
   lcd_command(0x28);&lt;br /&gt;
    &lt;br /&gt;
   // Display ein / Cursor aus / kein Blinken&lt;br /&gt;
   lcd_command(0x0C); &lt;br /&gt;
 &lt;br /&gt;
   // inkrement / kein Scrollen&lt;br /&gt;
   lcd_command(0x06);&lt;br /&gt;
&lt;br /&gt;
   lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
&lt;br /&gt;
void lcd_clear(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CLEAR_DISPLAY);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
&lt;br /&gt;
void lcd_home(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CURSOR_HOME);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)&lt;br /&gt;
&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t tmp;&lt;br /&gt;
&lt;br /&gt;
  switch (y) {&lt;br /&gt;
    case 1: tmp=0x80+0x00+x; break;    // 1. Zeile&lt;br /&gt;
    case 2: tmp=0x80+0x40+x; break;    // 2. Zeile&lt;br /&gt;
    case 3: tmp=0x80+0x10+x; break;    // 3. Zeile&lt;br /&gt;
    case 4: tmp=0x80+0x50+x; break;    // 4. Zeile&lt;br /&gt;
  }&lt;br /&gt;
  lcd_command(tmp);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_string(char *data)&lt;br /&gt;
{&lt;br /&gt;
    while(*data) {&lt;br /&gt;
        lcd_data(*data);&lt;br /&gt;
        data++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches die Funktionen benutzt, sieht zb. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen&lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    lcd_data(&#039;T&#039;);&lt;br /&gt;
    lcd_data(&#039;e&#039;);&lt;br /&gt;
    lcd_data(&#039;s&#039;);&lt;br /&gt;
    lcd_data(&#039;t&#039;);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
&lt;br /&gt;
    lcd_string(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist dabei, dass die Optimierung bei der Compilierung eingeschaltet ist, sonst stimmen die Zeiten der Funktionen _delay_us() und _delay_ms() nicht und der Code wird wesentlich länger (Siehe Dokumentation der libc im WinAVR).&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches eine Variable ausgibt, sieht zb. so aus.&lt;br /&gt;
Mittels der itoa() Funktion (itoa = &amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;nteger &amp;lt;b&amp;gt;To&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;A&amp;lt;/b&amp;gt;scii ) wird von einem Zahlenwert eine textuelle Repräsentierung ermittelt (sprich: ein String erzeugt) und dieser String mit der bereits vorhandenen Funktion lcd_string ausgegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen &lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Beispiel&lt;br /&gt;
int variable = 42;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe des Zeichens dessen ASCII-Code gleich dem Variablenwert ist&lt;br /&gt;
    // (Im Beispiel entspricht der ASCII-Code 42 dem Zeichen *)&lt;br /&gt;
    // http://www.code-knacker.de/ascii.htm&lt;br /&gt;
    lcd_data(variable);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
 &lt;br /&gt;
    // Ausgabe der Variable als Text in dezimaler Schreibweise&lt;br /&gt;
    {&lt;br /&gt;
       // ... umwandeln siehe FAQ Artikel bei www.mikrocontroller.net&lt;br /&gt;
       // WinAVR hat eine itoa()-Funktion, das erfordert obiges #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
       char Buffer[20]; // in diesem {} lokal&lt;br /&gt;
       itoa( variable, Buffer, 10 ); &lt;br /&gt;
&lt;br /&gt;
       // ... ausgeben  &lt;br /&gt;
       lcd_string( Buffer );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einrichten eines Projekts muss man zu der Datei mit dem Hauptprogramm auch die Datei lcd-routines.c in das Projekt aufnehmen. Dies geschieht beim AVR Studio unter Source Files im Fenster AVR GCC oder bei WinAVR im Makefile (z.B. durch SRC += lcd-routines.c).&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVRs sind für viele Steuerungsaufgaben zu schnell. Wenn wir beispielsweise eine LED oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die CPU-Frequenz verwenden, da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, Vorgänge in Zeitabständen durchzuführen, die geringer als die Taktfrequenz des Controllers sind. Selbstverständlich sollte die resultierende Frequenz auch noch möglichst genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist ganz einfach ein bestimmtes Register im µC, das völlig ohne Zutun des Programms, also per Hardware, hochgezählt wird. Das alleine wäre noch nicht allzu nützlich, wenn nicht dieses Hardwareregister bei bestimmten Zählerständen einen Interrupt auslösen könnte. Ein solches Ereignis ist der Overflow: Da die Bitbreite des Registers beschränkt ist, kommt es natürlich auch vor, dass der Zähler so hoch zählt, dass der nächste Zählerstand mit dieser Bitbreite nicht mehr darstellbar ist und der Zähler wieder auf 0 zurückgesetzt wird. Dieses Ereignis nennt man den Overflow und es ist möglich an dieses Ereignis einen Interrupt zu koppeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Alternativvorschlag mthomas &lt;br /&gt;
Jeder Timer verfügt über ein Zählerregister im Mikrocontroller, das automatisch und ohne Zutun des Programms von der Hardware weitergezählt wird. In einem einfachen Anwendungsfall stellt man den Timer auf eine Zählgeschwindigkeit (Frequenz) und kann dann anhand des Zählerstands ermitteln, wie viel Zeit vergangen ist. Das eigentlich Nützliche an Timern ist jedoch, dass man  bestimmte Zählerstände mit Interrupts verknüpfen kann, so dass der Controller beim Auftreten automatisch eine vom Anwender geschriebene Routine aufruft. Eines dieser möglichen Ereignis ist der Overflow ((Zähler-)Überlauf), der dann auftritt, wenn der Wert des Zählerregisters (Timer/Counter-Register) den maximal möglichen Wert überschreitet. Der Maximalwert wird durch die Bitbreite des Zählerregisters bestimmt (z.B. 255 bei 8-Bit Timern). Beim Überlauf/Overflow wird der Zähler durch die Hardware auf 0 zurückgesetzt und die Zählung beginnt von neuem. Wurde vorher der Overflow-Interrupt für den Timer aktiviert (im Timer Control Register) unterbricht der Controller automatisch die Ausführung des Hauptprogramms und verzweigt in die Interrupt-Routine des Anwenders.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auf den AT90S2313. Für andere Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer Auflösung von 65536. Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz, der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
== Der Vorteiler (Prescaler) ==&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler dient dazu, den CPU-Takt vorerst um einen einstellbaren Faktor zu reduzieren. Die so geteilte Frequenz wird den Eingängen der Timer zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024 einstellen, wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca. 4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw. Zählregister (TCNTx) mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle verfügen über mindestens einen, teilweise sogar zwei, 8-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird z.B bei AT90S2313 über folgende Register angesprochen (bei anderen Typen weitestgehend analog):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    TCCR0 |= (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst und die daran gebundene Interrupt-Routine abgearbeitet. Das TOV Flag &lt;br /&gt;
lässt sich durch das Hineinschreiben einer 1 und nicht wie erwartet einer 0 wieder zurücksetzen.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen außer den 8-Bit Timern auch 16-Bit Timer. Die 16-Bit Timer/Counter sind etwas komplexer aufgebaut als die 8-Bit Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM13&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM12 (CTC1)&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorteiler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt, und C der voreingestellte Vergleichswert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin T1, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin T1, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T1 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T1 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0, bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;TCNT1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;OCR1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;OCR1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;OCR1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;OCR1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäß folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]] [[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Ausserdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr. Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen kleiner 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4us warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.6 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t x;&lt;br /&gt;
&lt;br /&gt;
    x = 10;&lt;br /&gt;
&lt;br /&gt;
    while (x &amp;gt;= 0)&lt;br /&gt;
    {&lt;br /&gt;
      // tu was&lt;br /&gt;
&lt;br /&gt;
      x--;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog? ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Einige Controller haben einen eigenen Watchdog Oszillator, z.B. der Tiny2313 mit 128kHz. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde, beginnt der Counter von 0 an hochzuzählen. &lt;br /&gt;
Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern, müssen wir den Watchdog regelmäßig wieder neu starten bzw. rücksetzen (Watchdog Reset). &lt;br /&gt;
Dies sollte innerhalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern, muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten. Es müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. &lt;br /&gt;
Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist, wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei &#039;&#039;wdt.h&#039;&#039; (&#039;&#039;#include &amp;lt;avr/wdt.h&amp;gt;&#039;&#039;) in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch gestartet wird. &lt;br /&gt;
Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. &lt;br /&gt;
Falls ein anderer Wert gewünscht ist, so kann dies im Makfile in den Linker-Optionen eingetragen werden. &lt;br /&gt;
Dazu muss in der Zeile LDFLAGS folgende Option angefügt werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert&lt;br /&gt;
:Mögliche Timeoutwerte:&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Konstante&lt;br /&gt;
! Wert&lt;br /&gt;
! TimeOut&lt;br /&gt;
|- &lt;br /&gt;
| WDTO_15MS   &lt;br /&gt;
| 0&lt;br /&gt;
| 15 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_30MS   &lt;br /&gt;
| 1&lt;br /&gt;
| 30 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_60MS   &lt;br /&gt;
| 2&lt;br /&gt;
| 60 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_120MS   &lt;br /&gt;
| 3&lt;br /&gt;
| 120 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_250MS   &lt;br /&gt;
| 4&lt;br /&gt;
| 250 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_500MS   &lt;br /&gt;
| 5&lt;br /&gt;
| 500 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_1S   &lt;br /&gt;
| 6&lt;br /&gt;
| 1 S&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_2S   &lt;br /&gt;
| 7&lt;br /&gt;
| 2s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
== Watchdog-Anwendungshinweise ==&lt;br /&gt;
&lt;br /&gt;
Ob nun der Watchdog als Schutzfunktion überhaupt verwendet werden soll, hängt stark von der Anwendung, der genutzten Peripherie und dem Umfang und der Qualitätssicherung des Codes ab. Will man sicher gehen, dass ein Programm sich nicht in einer Endlosschleife verfängt, ist der Wachdog das geeignete Mittel dies zu verhindern. Weiterhin kann bei geschickter Programmierung der Watchdog dazu genutzt werden, bestimmte Stromsparfunktionen zu implementieren. Bei einigen neueren AVRs (z.B. dem ATTiny13) kann der Watchdog auch direkt als Timer genutzt werden, der den Controller aus einem Schlafmodus aufweckt. Auch dies kann im &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register eingestellt werden. Außerdem bietet der WD die einzige Möglichkeit einen beabsichtigten System-Reset (ein &amp;quot;richtiger Reset&amp;quot;, kein &amp;quot;jmp 0x0000&amp;quot;) ohne externe Beschaltung auszulösen, was z.B. bei der Implementierung eines Bootloaders nützlich ist. Bei bestimmten Anwendungen kann die Nutzung des WD als &amp;quot;ultimative Deadlock-Sicherung für nicht bedachte Zustände&amp;quot; natürlich immer als zusätzliche Sicherung dienen. &lt;br /&gt;
&lt;br /&gt;
Es besteht die Möglichkeit herauszufinden, ob ein Reset durch den Watchdog ausgelöst wurde (beim ATmega16 z.B. Bit WDRF in MCUCSR). Diese Information sollte auch genutzt werden, falls ein WD-Reset in der Anwendung nicht planmäßig implementiert wurde. Zum Beispiel kann man eine LED an einen freien Pin hängen, die nur bei einem Reset durch den WD aufleuchtet oder aber das &amp;quot;Ereignis WD-Reset&amp;quot; im internen EEPROM des AVR absichern, um die Information später z.B. über UART oder ein Display auszugeben (oder einfach den EEPROM-Inhalt über die ISP/JTAG-Schnittstelle auslesen).&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/77273#642501 Bug in ATtiny2313?]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt der Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft. Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe Abschnitt (TODO: verlinken) im Anhang.&lt;br /&gt;
&amp;lt;!--Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. vgl. [[AVR-GCC-Tutorial#Anhang|Anhang]])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.B. &#039;&#039;void TIMER0_OVF_vect(void)...&#039;&#039;). Der Unterschied im Vergleich zu ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben.  &amp;lt;!--Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)), als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur &lt;br /&gt;
in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechnungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechnungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0, PORTA ist danach 0b00000001. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-oder-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8 (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten &#039;&#039;Sleep-Modes&#039;&#039; (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_enable()&#039;&#039;&#039;&lt;br /&gt;
:aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_cpu()&#039;&#039;&#039;&lt;br /&gt;
: Versetzt den Controller in den Schlafmodus (sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt)&lt;br /&gt;
* &#039;&#039;&#039;sleep_disable()&#039;&#039;&#039;&lt;br /&gt;
:deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts.&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;allozieren&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich, um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory, bei AVR Controllern mit mehr als 64kB Flash auch elpm). Die Standard-Laufzeitbibliothek des avr-gcc (die avr-libc) stellt diese Funktionen nach Einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Lokale Variablen (eigentlich Konstanten) innerhalb von Funktionen können ebenfalls im Programmspeicher abgelegt werden. Dazu ist bei der Definition jedoch ein &#039;&#039;static&#039;&#039; voranzustellen, da solche &amp;quot;Variablen&amp;quot; nicht auf dem Stack bzw. (bei Optimierung) in Registern verwaltet werden können. Der Compiler &amp;quot;wirft&amp;quot; eine Warnung falls static fehlt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray2 };&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  static /*const*/ uint8_t pgmTestByteLocal PROGMEM = 0x55;&lt;br /&gt;
  static /*const*/ char pgmTestStringLocal[] PROGMEM = &amp;quot;im Flash&amp;quot;;&lt;br /&gt;
  // so nicht (static fehlt): char pgmTestStringLocalFalsch [] PROGMEM = &amp;quot;so nicht&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 // ...&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
    //...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte....&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWort);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;groß&amp;quot; (Stand avr-gcc 3.4.x). Damit ist der mögliche Speicherbereich für &amp;quot;Flash-Konstanten&amp;quot; auf 64kB begrenzt.  &amp;lt;!-- Einige avr-libc/pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher) (intern via Assembler Anweisung ELPM). Die Initialisierungswerde des Speicherinhalts jenseits der 64kB-Marke müssen dann jedoch auf anderem Weg angelegt werden (nicht PROGMEM, evtl. eigene Section und Linker-Optionen - TODO) /// alt - und nicht ganz korrekt: (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.)--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray enthält nun die Startadresse des Byte-Arrays pgmFooByteArray1&lt;br /&gt;
    // Allerdings würde ein direkter Zugriff mit diesem Pointer (z.B. temp=*ptrToArray)&lt;br /&gt;
    // &#039;&#039;&#039;nicht&#039;&#039;&#039; den Inhalt von pgmFooByteArray1[0] liefern, sondern von einer Speicherstelle&lt;br /&gt;
    // im &#039;&#039;&#039;RAM&#039;&#039;&#039;, die die gleiche Adresse hat wie pgmFooByteArray1[0]&lt;br /&gt;
    // Daher muss nun die Funktion pgm_read_byte() benutzt werden, die die in ptrToArray&lt;br /&gt;
    // enthaltene Adresse benutzt und auf das Flash zugreift.&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray enthält nun die Adresse des ersten Elements des Byte-Arrays pgmFooByteArray2&lt;br /&gt;
    // da im zweiten Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmFooByteArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Array aus Zeichenketten im Flash-Speicher ===&lt;br /&gt;
&lt;br /&gt;
Felder aus Zeichenketten im Flash-Speicher werden in zwei Schritten angelegt: Zuerst die einzelnen Elemente des Arrays und im Anschluss ein Array, in dem die Addressen der Zeichenketten abgelegt werden. Zum Auslesen wird zuerst die Adresse des i-ten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, auf das Element (die Zeichenkette) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const char str1[] PROGMEM = &amp;quot;first_A&amp;quot;;&lt;br /&gt;
const char str2[] PROGMEM = &amp;quot;second_A&amp;quot;;&lt;br /&gt;
const char str3[] PROGMEM = &amp;quot;third_A&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char *strarray1[] PROGMEM = {&lt;br /&gt;
	str1,&lt;br /&gt;
	str2,&lt;br /&gt;
	str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	int i, j, l;&lt;br /&gt;
	const char *pstrflash;&lt;br /&gt;
	char work[20], work2[20];&lt;br /&gt;
	// fuer Simulation: per volatile Optimierung verhindern, &lt;br /&gt;
	//                  da c nicht genutzt&lt;br /&gt;
	volatile char c;&lt;br /&gt;
	&lt;br /&gt;
	for ( i = 0; i &amp;lt; (sizeof(strarray1)/sizeof(strarray1[0]) ); i++ ) {&lt;br /&gt;
&lt;br /&gt;
		// setze Pointer auf die Addresse des i-ten Elements des&lt;br /&gt;
		// &amp;quot;Flash-Arrays&amp;quot; (str1, str2, ...)&lt;br /&gt;
		pstrflash = (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) );&lt;br /&gt;
		&lt;br /&gt;
		// kopiere den Inhalt der Zeichenkette von der&lt;br /&gt;
		// in pstrflash abgelegten Adresse in das work-Array&lt;br /&gt;
		// analog zu strcpy( work, strarray1[i]) wenn alles im RAM&lt;br /&gt;
		strcpy_P( work, pstrflash );&lt;br /&gt;
		// verkuerzt:&lt;br /&gt;
		strcpy_P( work2, (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) ) );&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
		// Zeichen-fuer-Zeichen&lt;br /&gt;
		l = strlen_P( pstrflash );&lt;br /&gt;
		for ( j=0; j &amp;lt; l; j++ ) {&lt;br /&gt;
			// analog zu c=strarray[i][j] wenn alles im RAM&lt;br /&gt;
			c = (char)( pgm_read_byte( pstrflash++ ) );&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	while (1) { ; }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: &amp;quot;How do I put an array of strings completely in ROM?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5)) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Vorsicht: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char textImFlashOK[] PROGMEM = &amp;quot;mit[]&amp;quot;; &lt;br /&gt;
// = Daten im &amp;quot;Flash&amp;quot;, textImFlashOK* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char* textImFlashProblem PROGMEM = &amp;quot;mit*&amp;quot;;&lt;br /&gt;
// Konflikt: Daten im BSS (lies: RAM), textImFlashFAIL* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der Initialisierungsstring von &amp;quot;textImFlashProblem&amp;quot; zu den Konstanten ans Ende des Programmcodes gelegt wird (BSS), von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte (und wird). Da der lesende Code (mittels pgm_read*) trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein weiters Problem des AVR-GCC (gesehen bei avr-gcc 3.4.1 und 3.4.2) bei der Anpassung an die Harvard-Architektur zu sein (konstanter Pointer auf variable Daten?!). Abhilfe (&amp;quot;Workaround&amp;quot;): Initialisierung bei Zeichenketten mit [] oder gleich im Code PSTR(&amp;quot;...&amp;quot;) nutzen.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden, ob es sich um eine Adresse im Flash oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind.&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die einen im Flash abgelegten String z.B. an eine UART ausgibt, würde dann so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void uart_puts_p(const char *text)&lt;br /&gt;
{&lt;br /&gt;
    char Zeichen;&lt;br /&gt;
&lt;br /&gt;
    while (Zeichen = pgm_read_byte(text))&lt;br /&gt;
    {   /* so lange, wie mittels pgm_read_byte ein Zeichen vom Flash gelesen&lt;br /&gt;
           werden konnte, welches nicht das &amp;quot;String-Endezeichen&amp;quot; darstellt */&lt;br /&gt;
&lt;br /&gt;
        /* Das gelesene Zeichen über die normalen Kanäle verschicken */&lt;br /&gt;
        uart_putc(Zeichen);&lt;br /&gt;
        text++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Flash in der Anwendung schreiben ===&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option (auch bekannt als Bootloader-Support) können Teile des Flash-Speichers auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. Bei wenigen &amp;quot;kleinen&amp;quot; AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm(Flash)- und Datenspeicher(RAM) auf. Der C-Standard und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiß&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, dass vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgegeben sind, die innerhalb weniger Taktzyklen aufeinanderfolgen müssen (&amp;quot;timed sequence&amp;quot;). Auch dies muss bei Nutzung der Funktionen aus der avr-libc/eeprom.h-Datei nicht selbst implementiert werden. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Siehe dazu folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt; // wird in aktuellen Versionen der avr-lib mit xx.h eingebunden&lt;br /&gt;
&lt;br /&gt;
// EEMEM wird bei aktuellen Versionen der avr-lib in eeprom.h definiert&lt;br /&gt;
// hier: definiere falls noch nicht bekannt (&amp;quot;alte&amp;quot; avr-libc)&lt;br /&gt;
#ifndef EEMEM&lt;br /&gt;
// alle Textstellen EEMEM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEMEM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
//...&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // &#039;Variablen&#039; eeFooByte geschrieben&lt;br /&gt;
//...&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z.B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    #define EEPROM_DEF 0xFF&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ( ( myByte = eeprom_read_byte(&amp;amp;eeFooByte) ) == EEPROM_DEF ) {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
//...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprüft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und möglicherweise noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,3);&lt;br /&gt;
&lt;br /&gt;
    /* dito etwas anschaulicher aber &amp;quot;unnütze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fließkommazahlen lassen sich recht praktisch über eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t i[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    uint8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEMEM;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
//...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklaration abgeleiteten EEPROM-Inhalt in eine Datei geschrieben werden (übliche Dateiendung: .eep, Daten im Intel Hex-Format). Damit können recht elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen (siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles). Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden (Write EEPROM), wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse (vgl. Datenblatt Abschnitt Fuse Bits) die vorherigen Daten (EESAVE programmed = 0), deren Position möglicherweise nicht mehr mit der Belegung im aktuellen Programm übereinstimmt oder den Standardwert nach &amp;quot;Chip Erase&amp;quot;: 0xFF (EESAVE unprogrammed = 1). Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Eine besondere Funktion des avr-gcc ist, dass mit entsprechenden Optionen im Makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Variable auf feste Adressen legen ===&lt;br /&gt;
&lt;br /&gt;
Gleich zu Beginn möchte ich darauf hinweisen, dass dieses Verfahren nur ein Workaround ist, mit dem man das Problem der anscheinend &amp;quot;zufälligen&amp;quot; Verteilung&lt;br /&gt;
der EEPROM-Variablen durch den Compiler etwas in den Griff bekommen kann.&lt;br /&gt;
&lt;br /&gt;
Hilfreich kann dies vor allem dann sein, wenn man z.B. über einen Kommandointerpreter (o.ä. Funktionen) direkt bestimmte EEPROM-Adressen manipulieren möchte. Auch wenn man über einen JTAG-Adapter (mk I oder mkII) den Programmablauf manipulieren möchte, indem man die EEPROM-Werte direkt ändert, kann diese Technik hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Im folgenden nun zwei Sourcelistings mit einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.h&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#inlcude &amp;lt;avr/eeprom.h&amp;gt;     // Die EEPROM-Definitionen/Macros der avr-libc einbinden&lt;br /&gt;
&lt;br /&gt;
#define   EESIZE   512      // Maximale Größe des EEPROMS&lt;br /&gt;
&lt;br /&gt;
#define   EE_DUMMY   0x000  // Dummyelement (Adresse 0 sollte nicht genutzt werden)&lt;br /&gt;
#define   EE_VALUE1  0x001  // Eine Bytevariable  &lt;br /&gt;
#define   EE_WORD1L  0x002  // Eine Wordvariable (Lowbyte)&lt;br /&gt;
#define   EE_WORD1H  0x003  // Eine Wordvariable (Highbyte)&lt;br /&gt;
#define   EE_VALUE2  0x004  // Eine weitere Bytevariable&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den Macros &#039;&#039;&#039;#define EE_VALUE1&#039;&#039;&#039; legt man den Namen und die Adresse der&lt;br /&gt;
&#039;Variablen&#039; fest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Die Adressen sollten fortlaufend, zumindest aber aufsteigend sortiert sein! Ansonsten besteht die Gefahr, daß man sehr schnell ein Durcheinander im EEPROM Speicher veranstaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Für den Compiler sind das lediglich Speicher-Adressen, über die auf das EEPROM zugegriffen wird. Der Compiler sieht nichts davon als eine echte Variable an und stößt sich daher auch nicht daran, wenn 2 Makros mit der gleichen Speicheradresse, bzw. überlappenden Speicherbereichen definiert werden. Es liegt einzig und alleine in der Hand des Programmierers, hier keinen Fehler zu machen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
   [EE_DUMMY]   = 0x00,&lt;br /&gt;
   [EE_VALUE1]  = 0x05,&lt;br /&gt;
   [EE_WORD1L]  = 0x01,   &lt;br /&gt;
   [EE_WORD1H]  = 0x00,&lt;br /&gt;
   [EE_VALUE2]  = 0xFF&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch die Verwendung eines Array, welches das gesammte EEPROM umfasst, bleibt&lt;br /&gt;
dem Compiler nicht anderes übrig, als das Array so zu platzieren, dass Element 0&lt;br /&gt;
des Arrays der Adresse 0 des EEPROMs entspricht. (&#039;&#039;Ich hoffe nur, dass die Compilerbauer daran nichts ändern!&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Wie man in dem obigen Codelisting auch sehen kann, hat das Verfahren einen kleinen Haken. Variablen die größer sind als 1 Byte, müssen etwas umständlicher&lt;br /&gt;
definiert werden. Benötigt man keine Initialisierung durch das Programm (was der Normalfall sein dürfte), dann kann man das auch so machen:&lt;br /&gt;
&lt;br /&gt;
Möchte man im EEPROM hintereinander beispielsweise Variablen, mit den Namen &#039;&#039;&#039;Wert&#039;&#039;&#039;, &#039;&#039;&#039;Anzahl&#039;&#039;&#039;, &#039;&#039;&#039;Name&#039;&#039;&#039; und &#039;&#039;&#039;Wertigkeit&#039;&#039;&#039; definieren, wobei Wert und Wertigkeit 1 Byte belegen sollen, Anzahl als 1 Wort (also 2 Bytes) und Name mit 10 Bytes reserviert werden soll, so geht auch folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes Makro definiert also seine Startadresse durch die Startadresse der unmittelbar vorhergehende &#039;Variablen&#039; plus der Anzahl der Bytes die von der vorhergehenden &#039;Variablen&#039; verbraucht werden. Dadurch ist man zumindest etwas auf der sicheren Seite, dass keine 2 &#039;Variablen&#039; im EEPROM überlappend definiert werden. Möchte man eine weitere &#039;Variable&#039; hinzufügen, so wird deren&lt;br /&gt;
Name, einfach anstelle der EE_LAST eingesetzt und eine neue Zeile für EE_LAST eingefügt, in der dann die Größe der &#039;Variablen&#039; festgelegt wird. Zb.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_PROZENT    ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( double ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EE_PROZENT legt die Startadresse für eine neue &#039;Variable&#039; des Datentyps double fest.&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf die EEPROM Werte kann dann z.B.so erfolgen:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t   temp1;&lt;br /&gt;
uint16_t  temp2;&lt;br /&gt;
&lt;br /&gt;
temp1 = eeprom_read_byte(EE_VALUE1);&lt;br /&gt;
temp2 = eeprom_read_word(EE_WORD1L);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ob die in der avr-libc vorhandenen Funktionen dafür verwendet werden können, weiß ich nicht. Aber in einigen Fällen muss man sich sowieso eigene Funktionen&lt;br /&gt;
bauen, welche die spezifischen Anforderungen (Interrupt - Atom Problem, etc.)&lt;br /&gt;
erfüllen.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist nur eine Möglichkeit, wie man dies realisieren kann. Sie bietet einem eine relativ einfache Art die EEPROM-Werte&lt;br /&gt;
auf beliebige Adressen zu legen oder Adressen zu ändern. Die Andere Möglichkeit besteht darin, die EEPROM-Werte wie folgt zu belegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
  0x00,                     //  ee_dummy&lt;br /&gt;
  0x05,                     //  ee_value1&lt;br /&gt;
  0x01,                     //  ee_word1L&lt;br /&gt;
  0x00,                     // (ee_word1H)&lt;br /&gt;
  0xFF                      //  ee_value2&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei kann man Variablen, die größer sind als 1 Byte einfacher definieren und&lt;br /&gt;
man muss nur die Highbyte- oder Lowbyte-Adresse in der &amp;quot;eeprom.h&amp;quot; definieren.&lt;br /&gt;
Allerdings muss man hier höllisch aufpassen, dass man nicht um eine oder mehrere&lt;br /&gt;
Positionen verrutscht!&lt;br /&gt;
&lt;br /&gt;
Welche der beiden Möglichkeiten man einsetzt, hängt vor allem davon ab, wieviele&lt;br /&gt;
Byte, Word und sonstige Variablen man benutzt. Gewöhnen sollte man sich an beide Varianten können ;)&lt;br /&gt;
&lt;br /&gt;
Kleine Schlussbemerkung:&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc unterstützt die Variante 1 und die Variante 2&lt;br /&gt;
* Der icc-avr Compiler unterstützt nur die Variante 2!&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Bei alten Versionen der avr-libc wurden nicht alle AVR Controller  untersützt. Z.B. bei der avr-libc Version 1.2.3 insbesondere bei AVRs &amp;quot;der neuen Generation&amp;quot; (ATmega48/88/168/169) funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register). In neueren Versionen (z.B. avr-libc 1.4.3 aus WinAVR 20050125) wurde die Zahl der unterstüzten Controller deutlich erweitert und eine Methode zur leichten Anpassung an zukünftige Controller eingeführt.&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Will oder kann man nicht auf die neue Version aktualisieren, kann der dort gezeigte Code auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;, gegebenfalls Schutz vor Unterbrechnung/Interrupt ergänzen &#039;&#039;uint8_t sreg; sreg=SREG; cli(); [EEPROM-Code] ; SREG=sreg; return;&#039;&#039;, siehe Abschnitt Interrupts). Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== EEPROM Register ===&lt;br /&gt;
Um das EEPROM anzusteuern sind drei Register von Bedeutung.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEAR Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL da in einem 8 Bit Register keine 512 Adressen adressiert werden können&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEDR Hier werden die Daten eingetragen die geschrieben werden sollen bzw. es enthält die gelesenen Daten&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EECR Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TABLE BORDER=1&amp;gt;&lt;br /&gt;
&amp;lt;TR&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;Bit&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;1&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;0&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&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;Name&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&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;TD&amp;gt;-&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERIE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEMWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERE&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;Read/Write&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;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&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;Init Value&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;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;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;TD&amp;gt;0&amp;lt;/TD&amp;gt;&lt;br /&gt;
&amp;lt;/TR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/TABLE&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bedeutung der Bits:&amp;lt;/U&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4-7 nicht belegt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 3 (EERIE) - EEPROM Ready Interrupt Enable:&amp;lt;/U&amp;gt; Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7) wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0 wird kein Interrupt ausgelöst.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 2(EEMWE) - EEPROM Master Write Enable:&amp;lt;/U&amp;gt; Dieses Bit bestimmt, daß wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE =0 ist und EEWE = 1 gesetzt wird hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst.&lt;br /&gt;
Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 1 (EEWE) - EEPROM Write Enable:&amp;lt;/U&amp;gt; Dieses Bit löst den Schreibvorgang aus wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist wird dieses Bit automatisch wieder auf 0 gesetzt und sofern EERIE gesetzt ist ein Interrupt ausgelöst. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ein Schreibvorgang sieht typischerweise wie folgt aus:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. EEPROM Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Daten übergeben an EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1 &amp;lt;BR&amp;gt;&lt;br /&gt;
5. (Optinal) Warten bis Schreibvorgang abgeschlossen ist.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 0 EERE – EEPROM Read Enable:&amp;lt;/U&amp;gt; Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit wenn das Bit EEWE=0 ist. ISt der LEsevorgang abgeschlossen wird das Bit wieder auf 0 gesetzt und das EEPROM ist für neue Lese/Schreibbefehle wieder bereit.&amp;lt;BR&amp;gt;&lt;br /&gt;
Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. Bereitschaft zum lesen prüfen (EEWE=0)&amp;lt;BR&amp;gt;&lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Lesezyklus auslösen mit EERE = 1&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Warten bis Lesevorgang abgeschlossen EERE = 0&amp;lt;BR&amp;gt;&lt;br /&gt;
5. Daten abholen aus EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bietet sich printf/sprintf an. &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t s[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( s, &amp;quot;Zählerstand: %d&amp;quot;, value );&lt;br /&gt;
    uart_puts( s );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, die Ausgabe stdout auf eigene Ausgabefunktionen umzuleiten. Dazu wird dem Ausgabemechanismus eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben (wie auch immer die Ausgabe stattfindet). Alle anderen, höheren, Funktionen greifen letztendlich auf diese eine Ausgabefunktion zurück, die die Ausgabe eines einzelnen Zeichens vornimmt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uart_init();&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
    printf( &amp;quot;Hello, world!\n&amp;quot; );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil aller printf-Varianten: Sie sind recht speicherintensiv.&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C- und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gnu-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Inline-Assembler: Die Assembleranweisungen werden direkt in den C-Code integriert. Eine Quellcode-Datei enthält somit C- und Assembleranweisungen&lt;br /&gt;
* Assembler-Dateien: Der Assemblercode befindet sich in eigenen Quellcodedateien. Diese werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung (NOP steht für No Operation). Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=Warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
   /* Verzögern der weiteren Programmausführung um&lt;br /&gt;
      genau 3 Taktzyklen */&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeintlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
&lt;br /&gt;
    /* leere Schleife - wird bei eingeschalteter Compiler-Optimierung   wegoptimiert */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* alternative Methode (keine Optimierung): */&lt;br /&gt;
    volatile uint16_t j;&lt;br /&gt;
    for (j = 0; j &amp;lt; 1000; j++)&lt;br /&gt;
      ;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützlicher &amp;quot;Assembler-Einzeiler&amp;quot; ist der Aufruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;);&#039;&#039;), da hierzu in älteren Versionen der avr-libc keine eigene Funktion existiert (in neueren Versionen &#039;&#039;sleep_cpu()&#039;&#039; aus sleep.h).&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise Delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil, dass die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static inline void delayloop16 (uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
    asm volatile (&amp;quot;cp  %A0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;cpc %B0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;breq 2f               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;1:                    \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;sbiw %0,1             \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;brne 1b               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;2:                    &amp;quot;  &lt;br /&gt;
                  : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
	          : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
    );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Anweisung wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen. Der Zeilenumbruch teilt dem Assembler mit, dass ein neuer Befehl beginnt.&lt;br /&gt;
* Als Sprung-Marken (Labels) werden Ziffern verwendet. Diese speziellen Labels sind mehrfach im Code verwendbar. Gesprungen wird jeweils zurück (b) oder vorwärts (f) zum nächsten ausffindbaren Label.&lt;br /&gt;
&lt;br /&gt;
Das Resultat zeigt ein Blick in die Assembler-Datei, die der Compiler mit der option &amp;lt;tt&amp;gt;-save-temps&amp;lt;/tt&amp;gt; nicht löscht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	cp  r24, __zero_reg__ 	 ;  count&lt;br /&gt;
	cpc r25, __zero_reg__ 	 ;  count&lt;br /&gt;
	breq 2f               &lt;br /&gt;
	1:                    &lt;br /&gt;
	sbiw r24,1             	 ;  count&lt;br /&gt;
	brne 1b               &lt;br /&gt;
	2:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc im Abschnitt [http://www.nongnu.org/avr-libc/user-manual/inline_asm.html Related Pages/Inline Asm]. &lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc Deutsche Einführung in Inline-Assembler]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
Wenn man mit dem AVR Studio arbeitet, kann alternativ auch das standardmäßig erstellte Makefile bearbeitet und folgende Zeilen eingefügt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
## Objects that must be built in order to link&lt;br /&gt;
OBJECTS = (alte Dateien...) useful.o&lt;br /&gt;
&lt;br /&gt;
## Compile&lt;br /&gt;
## Hier folgt eine Liste der gelinkten Dateien, darunter einfügen:&lt;br /&gt;
useful.o: ../useful.S&lt;br /&gt;
	$(CC) $(INCLUDES) $(ASMFLAGS) -c  $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Das war es schon. Allerdings gilt es zu beachten, dass das makefile über &amp;quot;Project -&amp;gt; Configuration options&amp;quot; selbst einzubinden ist, sonst wird es natürlich wieder überschrieben.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel eine Funktion &#039;&#039;superFunc&#039;&#039;, die alle Pins des Ports D auf &amp;quot;Ausgang&amp;quot; schaltet, eine Funktion &#039;&#039;ultraFunc&#039;&#039;, die die Ausgänge entsprechend des übergebenen Parameters schaltet, eine Funktion &#039;&#039;gigaFunc&#039;&#039;, die den Status von Port A zurückgibt und eine Funktion &#039;&#039;addFunc&#039;&#039;, die zwei Bytes zu einem 16-bit-Wort addiert. Die Zuweisungen im C-Code (PORTx = ...) verhindern, dass der Compiler die Aufrufe wegoptimiert und dienen nur zur Veranschaulichung der Parameterübergaben.&lt;br /&gt;
&lt;br /&gt;
Zuerst der Assembler-Code. Der Dateiname sei useful.S:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//; Arbeitsregister (ohne &amp;quot;r&amp;quot;) &lt;br /&gt;
workreg  = 16&lt;br /&gt;
workreg2 = 17&lt;br /&gt;
&lt;br /&gt;
//; Konstante:&lt;br /&gt;
ALLOUT = 0xff&lt;br /&gt;
&lt;br /&gt;
//; ** Setze alle Pins von PortD auf Ausgang **&lt;br /&gt;
//; keine Parameter, keine Rückgabe&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg  // beachte: _SFR_IO_ADDR()&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Setze PORTD auf übergebenen Wert **&lt;br /&gt;
//; Parameter in r24 (LSB immer bei &amp;quot;graden&amp;quot; Nummern)&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zustand von PINA zurückgeben **&lt;br /&gt;
//; Rückgabewerte in r24:r25 (LSB:MSB), hier nur LSB genutzt&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zwei Bytes addieren und 16-bit-Wort zurückgeben **&lt;br /&gt;
//; Parameter in r24 (Summand1) und r22 (Summand2) -&lt;br /&gt;
//;  Parameter sind Word-&amp;quot;aligned&amp;quot; d.h. LSB immer auf &amp;quot;graden&amp;quot;&lt;br /&gt;
//;  Registernummern. Bei 8-Bit und 16-Bit Paramtern somit &lt;br /&gt;
//;  beginnend bei r24 dann r22 dann r20 etc.&lt;br /&gt;
//; Rückgabewert in r24:r25&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   push workreg2&lt;br /&gt;
   clr workreg2&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
   pop workreg2&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
//; oh je - sorry - Mein AVR-Assembler ist eingerostet, hoffe das stimmt so...&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Makefile ist der Name der Assembler-Quellcodedatei einzutragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
ASRC = useful.S&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Aufruf erfolgt dann im C-Code so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
extern void superFunc(void);&lt;br /&gt;
extern void ultraFunc(uint8_t setVal);&lt;br /&gt;
extern uint8_t gigaFunc(void);&lt;br /&gt;
extern uint16_t addFunc(uint8_t w1, uint8_t w2);&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
[...]&lt;br /&gt;
  superFunc();&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
&lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
[...]&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird wieder in der lss-Datei ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
   superFunc();&lt;br /&gt;
 148:	0e 94 f6 00 	call	0x1ec&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
 14c:	85 e5       	ldi	r24, 0x55	; 85&lt;br /&gt;
 14e:	0e 94 fb 00 	call	0x1f6&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
 152:	0e 94 fd 00 	call	0x1fa&lt;br /&gt;
 156:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
  &lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
 158:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 15a:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 15c:	0e 94 ff 00 	call	0x1fe&lt;br /&gt;
 160:	8b bb       	out	0x1b, r24	; 27&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
 162:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 164:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 166:	0e 94 fc 00 	call	0x1f8&lt;br /&gt;
 16a:	89 2f       	mov	r24, r25&lt;br /&gt;
 16c:	99 27       	eor	r25, r25&lt;br /&gt;
 16e:	88 bb       	out	0x18, r24	; 24&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
000001ec &amp;lt;superFunc&amp;gt;:&lt;br /&gt;
// setze alle Pins von PortD auf Ausgang&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1ec:	0f 93       	push	r16&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
 1ee:	0f ef       	ldi	r16, 0xFF	; 255&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg&lt;br /&gt;
 1f0:	01 bb       	out	0x11, r16	; 17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 1f2:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 1f4:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001f6 &amp;lt;ultraFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// setze PORTD auf übergebenen Wert&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
 1f6:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
   ret&lt;br /&gt;
 1f8:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fa &amp;lt;gigaFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Zustand von PINA zurückgeben&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
 1fa:	89 b3       	in	r24, 0x19	; 25&lt;br /&gt;
   ret&lt;br /&gt;
 1fc:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fe &amp;lt;addFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// zwei Bytes addieren und 16-bit-Wort zurückgeben&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1fe:	0f 93       	push	r16&lt;br /&gt;
   push workreg2&lt;br /&gt;
 200:	1f 93       	push	r17&lt;br /&gt;
   clr workreg2&lt;br /&gt;
 202:	11 27       	eor	r17, r17&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
 204:	06 2f       	mov	r16, r22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
 206:	08 0f       	add	r16, r24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
 208:	11 1d       	adc	r17, r1&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
 20a:	c8 01       	movw	r24, r16&lt;br /&gt;
   pop workreg2&lt;br /&gt;
 20c:	1f 91       	pop	r17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 20e:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 210:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zuweisung von Registern zu Parameternummer und die Register für die Rückgabewerte sind in den &amp;quot;Register Usage Guidelines&amp;quot; der avr-libc-Dokumentation erläutert.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/assembler.html avr-libc-Dokumentation: Related Pages/avr-libc and assembler programs]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage avr-libc-Dokumentation: Related Pages/FAQ/&amp;quot;What registers are used by the C compiler?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
== Globale Variablen für Datenaustausch ==&lt;br /&gt;
&lt;br /&gt;
Oftmals kommt man um globale Variablen nicht herum, z.B. um den Datenaustausch zwischen Hauptprogramm und Interrupt-Routinen zu realisieren. &lt;br /&gt;
Hierzu muss man im Assembler wissen, wo genau die Variable vom C-Compiler abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
Hierzu muss die Variable, hier &amp;quot;zaehler&amp;quot; genannt, zuerst im C-Code als Global definiert werden, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
int16_t main (void)&lt;br /&gt;
{&lt;br /&gt;
    // irgendein Code, in dem zaehler benutzt werden kann&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im foldenden Assembler-Beispiel wird der Externe Interrupt0  verwendet, um den Zähler hochzuzählen. Es fehlen die Initialisierungen des Interrupts und die Interrupt-Freigabe, so richtig sinnvoll ist der Code auch nicht, aber er zeigt (hoffentlich) wie es geht.&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit Interrupt-Vektoren gilt beim GCC-Assembler das Gleiche, wie bei C: Man muss die exakte Schreibweise beachten, ansonsten wird nicht der Interrupt-Vektor angelegt, sondern eine neue Funktion - und man wundert sich, dass nichts funktionert (vgl. das AVR-GCC-Handbuch).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
.extern zaehler&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      //; wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     //; Status-Register (SREG) sichern!&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     lds temp,zaehler               //; Wert aus dem Speicher lesen&lt;br /&gt;
     inc temp                       //; bearbeiten&lt;br /&gt;
     sts zaehler,temp               //; und wieder zurückschreiben&lt;br /&gt;
&lt;br /&gt;
     pop temp                       //; die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen im Assemblerfile anlegen ===&lt;br /&gt;
&lt;br /&gt;
Alternativ können Variablen aber auch im Assemblerfile angelegt werden. Dadurch kann auf eine .c-Datei verzichtet werden. Für das obige Beispiel könnte der Quelltext dann die Dateien zaehl_asm.S und zaehl_asm.h abgelegt werden, so dass nur noch zaehl_asm.S mit kompiliert werden müsste.&lt;br /&gt;
&lt;br /&gt;
Anstatt im Assemblerfile über das Schlüsselwort &#039;&#039;.extern &#039;&#039; auf eine vorhandene Variable zu verweisen, wird dazu mit dem Schlüsselwort &#039;&#039;.comm&#039;&#039; die benötigte Anzahl von Bytes für eine Variable reserviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.S&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
//; 1 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 1&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Headerdatei wird dann auf die Variable nur noch verwiesen (Schlüsselwort &#039;&#039;extern&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef ZAEHL_ASM_H&lt;br /&gt;
#define ZAEHL_ASM_H&lt;br /&gt;
&lt;br /&gt;
extern volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu globalen Variablen in C werden so angelegte Variablen nicht automatisch mit dem Wert 0 initialisiert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer als 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Variablen, die größer als &#039;&#039;&#039;ein&#039;&#039;&#039; Byte sind, können in Assembler auf ähnliche Art angesprochen werden. Hierzu müssen nur genug Bytes angefordert werden, um die Variable aufzunehmen. Soll z.B. für den Zähler eine Variable vom Typ &#039;&#039;unsigned long&#039;&#039;, also &#039;&#039;uint32_t&#039;&#039; verwendet werden, so müssen 4 Bytes reserviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die dazugehörige Deklaration im Headerfile wäre dann:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
extern volatile uint32_t zaehler;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen, die größer als ein Byte sind, werden die Werte beginnend mit dem niederwertigsten Byte im RAM abgelegt. Das folgende Codeschnippsel zeigt, wie unter Assembler auf die einzelnen Bytes zugegriffen werden kann. Dazu wird im Interrupt nun ein 32-Bit Zähler erhöht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      // wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     // Status-Register (SREG) sichern !&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     // 32-Bit-Zähler incrementieren&lt;br /&gt;
     lds temp, (zaehler + 0)        // 0. Byte (niederwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 0), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
     lds temp, (zaehler + 1)        // 1. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 1), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 2)        // 2. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 2), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 3)        // 3. Byte (höchstwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 3), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
RAUS:&lt;br /&gt;
     pop temp                       // die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TODO:&#039;&#039;&#039; 16-Bit / 32-Bit Variablen, Zugriff auf Arrays (Strings)&lt;br /&gt;
&lt;br /&gt;
= Anhang =&lt;br /&gt;
&lt;br /&gt;
== Besonderheiten bei der Anpassung bestehenden Quellcodes ==&lt;br /&gt;
&lt;br /&gt;
Einige Funktionen, die in frühren Versionen der avr-libc vorhanden waren, werden inzwischen als veraltet angesehen. Sie sind nicht mehr vorhanden oder als &#039;&#039;deprecated&#039;&#039; (missbilligt) ausgewiesen und Definitionen in &amp;lt;compat/deprecated.h&amp;gt; verschoben. Es empfiehlt sich, vorhandenen Code zu portieren und die alten Funktionen nicht mehr zu nutzen, auch wenn diese noch zur Verfügung stehen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zur Deklaration von Interrupt-Routinen ===&lt;br /&gt;
&lt;br /&gt;
Die Funktionen (eigentlich Makros) &#039;&#039;SIGNAL&#039;&#039; und &#039;&#039;INTERRUPT&#039;&#039; zur Deklaration von Interruptroutinen sollten nicht mehr genutzt werden. &lt;br /&gt;
&lt;br /&gt;
In aktuellen Versionen der avr-libc (z.B. avr-libc 1.4.3 aus WinAVR 20060125) werden Interruptroutinen, die &#039;&#039;&#039;nicht&#039;&#039;&#039; durch andere Interrupts &#039;&#039;&#039;unterbrechbar&#039;&#039;&#039; sind, mit ISR deklariert (siehe Abschnitt im Hauptteil). Auch die Benennung wurden vereinheitlicht und an die üblichen Bezeichnungen in den AVR Datenblättern angepasst. In der Dokumentation der avr-libc sind alte und neue Bezeichnungen in der Tabelle gegenübergestellt. Die erforderlichen Schritte zur Portierung:&lt;br /&gt;
&lt;br /&gt;
* #include von avr/signal.h entfernen&lt;br /&gt;
* SIGNAL duch ISR ersetzen&lt;br /&gt;
* Name des Interrupt-Vektors anpassen (SIG_* durch entsprechendes *_vect)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für die Anpassung zuerst ein &amp;quot;alter&amp;quot; Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer2 Output Compare bei einem ATmega8 */&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE2)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt wird der Vektor mit TIMER2 COMP bezeichnet. Die Bezeichnung in der avr-libc entspricht dem Namen im Datenblatt, Leerzeichen werden durch Unterstriche (_) ersetzt und ein _vect angehängt. &lt;br /&gt;
&lt;br /&gt;
Der neue Code sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt; &lt;br /&gt;
/* signal.h entfällt */&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER2_COMP_vect)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Unklarheiten bezüglich der neuen Vektorlabels hilft (noch) ein Blick in die Headerdatei des entsprechenden Controllers. Für das vorherige Beispiel also der Blick in die Datei iom8.h für den ATmega8, dort findet man die veraltete Bezeichnung unterhalb der aktuellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect		_VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2		_VECTOR(3)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Nachfolgendes mit avr-libc 1.4.5 (in WinAVR 1/2007 behoben - noch eine Weile auskommentiert lassen und dann löschen.&lt;br /&gt;
Konnte in alten Versionen signal.h ohne interrupt.h eingebunden werden, erhält man bei Verwendung der avr-libc Version 1.4.3 (WinAVR 2/2005) beim Compilieren eine Fehlermeldung, da mit signal.h nicht die erforderlichen Definitionen eingebunden werden. Der Lösungsvorschlag in signal.h auch interrupt.h einzubinden, wurde von den avr-libc-Enwicklern akzeptiert und das Problem ist  im Quellcode (CVS) bereits behoben. Es ist aber noch keine avr-libc-&amp;quot;Release&amp;quot; bzw. noch kein WinAVR mit dieser avr-libc-Korrektur verfügbar (Stand 5.2.2006). Will oder kann man den Quellcode nicht aktualisieren, gibt es folgende Alternativen:&lt;br /&gt;
* in Quellcodedateien, in denen nur avr/signal.h eingebunden wird, interrupt.h einbinden (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;). signal.h weiterhin einbinden, falls Kompatibiltät mit alten Versionen gewünscht ist.&lt;br /&gt;
* in der Datei signal.h (bein WinAVR in c:/WinAVR/avr/include/avr/signal.h) ein (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;) ergänzen --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;unterbrechbare&#039;&#039;&#039; Interruptroutinen, die mittels &#039;&#039;INTERRUPT&#039;&#039; deklariert sind, gibt es keinen direkten Ersatz in Form eines Makros. Solche Routinen sind laut Dokumentation der avr-libc in folgender Form zu deklarieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* ** alt ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
INTERRUPT(SIG_OVERFLOW0)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ** neu: ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void TIMER0_OVF_vect(void) __attribute__((interrupt));&lt;br /&gt;
void TIMER0_OVF_vect(void) &lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von &#039;&#039;INTERRUPT&#039;&#039; die Header-Datei &#039;&#039;compat/deprecated.h&#039;&#039; einzubinden. Man sollte bei dieser Gelegenheit jedoch nochmals überprüfen, ob die Funktionalität von &#039;&#039;INTERRUPT&#039;&#039; tatsächlich gewollt ist. In vielen Fällen wurde &#039;&#039;INTERRUPT&#039;&#039; dort genutzt, wo eigentlich &#039;&#039;SIGNAL&#039;&#039; (nunmehr &#039;&#039;ISR&#039;&#039;) hätte genutzt werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;inp&#039;&#039; und &#039;&#039;outp&#039;&#039; zum Einlesen bzw. Schreiben von Registern sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
unsigned char i, j;&lt;br /&gt;
&lt;br /&gt;
// alt:&lt;br /&gt;
  i = inp(PINA);&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  outp(PORTB, j);&lt;br /&gt;
&lt;br /&gt;
// neu (nicht mehr wirklich neu...):&lt;br /&gt;
  i = PINA&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  PORTB = j;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von inp und outp die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Zugriff auf Bits in Registern ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;cbi&#039;&#039; und &#039;&#039;sbi&#039;&#039; zum Löschen und Setzen von Bits sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg. Die Bezeichnung ist ohnehin irreführend da die Funktionen nur für Register mit Adressen im unteren Speicherbereich tatsächlich in die Assembleranweisungen cbi und sbi übersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// alt:&lt;br /&gt;
  sbi(PORTB, PB2);&lt;br /&gt;
  cbi(PORTC, PC1);&lt;br /&gt;
&lt;br /&gt;
// neu (auch nicht mehr wirklich neu...):&lt;br /&gt;
  PORTB |=  (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
  PORTC &amp;amp;= ~(1&amp;lt;&amp;lt;PC1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von sbi und cbi die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden. Wer unbedingt will, kann sich natürlich eigene Makros mit aussagekräftigeren Namen definieren. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &amp;amp;= ~(1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Selbstdefinierte (nicht-standardisierte) ganzzahlige Datentypen ===&lt;br /&gt;
&lt;br /&gt;
Bei den im Folgenden genannten Typdefinitionen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich, bei der Überarbeitung von altem Code die im Abschnitt &#039;&#039;standardisierten ganzzahligen Datentypen&#039;&#039; beschriebenen Datentypen zu nutzen (stdint.h) und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef unsigned char      BYTE;       // besser: uint8_t  aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned short     WORD;       // besser: uint16_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long      DWORD;      // besser: uint32_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long long QWORD;      // besser: uint64_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; BYTE : Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 255. &lt;br /&gt;
&lt;br /&gt;
; WORD : Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 65535. &lt;br /&gt;
&lt;br /&gt;
; DWORD : Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
; QWORD : Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Funktionen im Makefile ==&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken (Libraries/.a-Dateien) hinzufügen ===&lt;br /&gt;
&lt;br /&gt;
Um Funktionen aus Bibliotheken (&amp;quot;echte&amp;quot; Libraries, *.a-Dateien) zu nutzen, sind dem Linker die Namen der Bibliotheken als Parameter zu übergeben. Dazu ist die Option -l (kleines L) vorgesehen, an die der Name der Library angehängt wird. &lt;br /&gt;
&lt;br /&gt;
Dabei ist zu beachten, dass der Name der Library und der Dateiname der Library nicht identisch sind. Der hinter -l angegebene Name entspricht dem Dateinamen der Library ohne die Zeichenfolge &#039;&#039;lib&#039;&#039; am Anfang des Dateinamens und ohne die Endung &#039;&#039;.a&#039;&#039;. Sollen z.B. Funktionen aus einer Library mit dem Dateinamen &#039;&#039;libefsl.a&#039;&#039; eingebunden (gelinkt) werden, lautet der entsprechende Parameter -lefsl (vergl. auch -lm zum Anbinden von libm.a). &lt;br /&gt;
&lt;br /&gt;
In Makefiles wird traditonell eine make-Variable LDLIBS genutzt, in die &amp;quot;l-Parameter&amp;quot; abgelegt werden. Die WinAVR-makefile-Vorlage enthält diese Variable zwar nicht, dies stellt jedoch keine Einschränkung dar, da alle in der make-Variable LDFLAGS abgelegten Parameter an den Linker weitergereicht werden. &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Einbinden von Funktionen aus einer Library efsl (Dateiname libefsl.a)&lt;br /&gt;
LDFLAGS += -lefsl&lt;br /&gt;
# Einbinden von Funktionen aus einer Library xyz (Dateiname libxyz.a)&lt;br /&gt;
LDFLAGS += -lxyz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Liegen die Library-Dateien nicht im Standard Library-Suchpfad, sind die Pfade mittels Parameter &#039;&#039;-L&#039;&#039; ebenfalls anzugeben. (Der vordefinierte Suchpfad kann mittels &#039;&#039;avr-gcc --print-search-dirs&#039;&#039; angezeigt werden.)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Projekt (&amp;quot;superapp2&amp;quot;), in dem der Quellcode von zwei Libraries (efsl und xyz) und der Quellcode der eigentlichen Anwendung in verschiedenen Verzeichnissen mit der folgenden &amp;quot;Baumstruktur&amp;quot; abgelegt sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
superapp2&lt;br /&gt;
|&lt;br /&gt;
+----- efslsource (darin libefsl.a)&lt;br /&gt;
|&lt;br /&gt;
+----- xyzsource (darin libxyz.a)&lt;br /&gt;
|&lt;br /&gt;
+----- firmware (darin Anwendungs-Quellcode und Makefile)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt, dass im Makefile die Verzeichnis efslsource und xyzsource in den Library-Suchpfad aufzunehmen sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
LDFLAGS += -L../efslsource/ -L../xyzsource/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fuse-Bits ===&lt;br /&gt;
&lt;br /&gt;
Zur Berechnung der Fuse-Bits bietet sich neben dem Studium des Datenblattes auch der [http://palmavr.sourceforge.net/cgi-bin/fc.cgi AVR Fuse Calculator] an. Gewarnt werden muss vor der Benutzung von PonyProg, weil dort durch die negierte Darstellung gern Fehler gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Soll die Programmierung von Fuse- und Lockbits automatisiert werden, kann man dies ebenfalls durch Einträge im Makefile vornehmen, die beim Aufruf von &amp;quot;make program&amp;quot; an die genutzte Programmiersoftware übergeben werden. In der makefile-Vorlage von WinAVR (und mfile) gibt es dafuer jedoch keine &amp;quot;Ausfüllhilfe&amp;quot; (Stand 9/2006). Die folgenden Ausführungen gelten für die Programmiersoftware [[AVRDUDE]] (Standard in der WinAVR-Vorlage), können jedoch sinngemäß auf andere Programmiersoftware übertragen werden, die die Angabe der Fuse- und Lockbits-Einstellungen per Kommandozeilenparameter unterstützt (z.B. stk500.exe). Im einfachsten Fall ergänzt man im Makefile einige Variablen, deren Werte natürlich vom verwendeten Controller und den gewünschten Einstellungen abhängen (vgl. Datenblatt Fuse-/Lockbits):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#---------------- Programming Options (avrdude) ----------------&lt;br /&gt;
&lt;br /&gt;
#...&lt;br /&gt;
#Beispiel! f. ATmega16 - nicht einfach uebernehmen! Zahlenwerte anhand&lt;br /&gt;
#--------- des Datenblatts nachvollziehen und gegebenenfalls aendern.&lt;br /&gt;
#&lt;br /&gt;
AVRDUDE_WRITE_LFUSE = -U lfuse:w:0xff:m&lt;br /&gt;
AVRDUDE_WRITE_HFUSE = -U hfuse:w:0xd8:m&lt;br /&gt;
AVRDUDE_WRITE_LOCK  = -U lock:w:0x2f:m&lt;br /&gt;
#...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit diese Variablen auch genutzt werden, ist der Aufruf von avrdude im Makefile entsprechend zu ergänzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Program the device.  &lt;br /&gt;
program: $(TARGET).hex $(TARGET).eep&lt;br /&gt;
# ohne Fuse-/Lock-Einstellungen (nach WinAVR Vorlage Stand 4/2006)&lt;br /&gt;
#	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
#        $(AVRDUDE_WRITE_EEPROM)&lt;br /&gt;
# mit Fuse-/Lock-Einstellungen&lt;br /&gt;
        $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_LFUSE) \&lt;br /&gt;
        $(AVRDUDE_WRITE_HFUSE) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
        $(AVRDUDE_WRITE_EEPROM) $(AVRDUDE_WRITE_LOCK)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weiter Möglichkeit besteht darin, die Fuse- und Lockbit-Einstellungen vom Preprozessor/Compiler generieren zu lassen. Die Fuse-Bits werden dann bei Verwendung von AVRDUDE in eigene Hex-Files geschrieben. Hierzu kann man z.B. folgendes Konstrukt verwenden:&lt;br /&gt;
&lt;br /&gt;
In eine der C-Sourcen wird eine Variable je Fuse-Byte vom Typ &#039;&#039;unsigned char&#039;&#039; deklariert und in eine extra Section gepackt. Dies kann entweder in einem vorhandenen File passieren oder in ein neues (z.B. fuses.c) geschrieben werden. Das File muss im Makefile aber auf jeden Fall mit kompiliert und gelinkt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// tiny 2313 fuses low byte&lt;br /&gt;
#define CKDIV8  7&lt;br /&gt;
#define CKOUT   6&lt;br /&gt;
#define SUT1    5&lt;br /&gt;
#define SUT0    4&lt;br /&gt;
#define CKSEL3  3&lt;br /&gt;
#define CKSEL2  2&lt;br /&gt;
#define CKSEL1  1&lt;br /&gt;
#define CKSEL0  0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses high byte&lt;br /&gt;
#define DWEN       7&lt;br /&gt;
#define EESAVE     6&lt;br /&gt;
#define SPIEN      5&lt;br /&gt;
#define WDTON      4&lt;br /&gt;
#define BODLEVEL2  3&lt;br /&gt;
#define BODLEVEL1  2&lt;br /&gt;
#define BODLEVEL0  1&lt;br /&gt;
#define RSTDISBL   0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses extended byte&lt;br /&gt;
#define SELFPRGEN  0&lt;br /&gt;
&lt;br /&gt;
#define LFUSE         __attribute__ ((section (&amp;quot;lfuses&amp;quot;)))&lt;br /&gt;
#define HFUSE         __attribute__ ((section (&amp;quot;hfuses&amp;quot;)))&lt;br /&gt;
#define EFUSE         __attribute__ ((section (&amp;quot;efuses&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// select ext crystal 3-8Mhz&lt;br /&gt;
unsigned char lfuse LFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;CKDIV8) | (1&amp;lt;&amp;lt;CKOUT) | (1&amp;lt;&amp;lt;CKSEL3) | (1&amp;lt;&amp;lt;CKSEL2) | &lt;br /&gt;
      (0&amp;lt;&amp;lt;CKSEL1) | (1&amp;lt;&amp;lt;CKSEL0) | (0&amp;lt;&amp;lt;SUT1) | (1&amp;lt;&amp;lt;SUT0) );&lt;br /&gt;
unsigned char hfuse HFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;DWEN) | (1&amp;lt;&amp;lt;EESAVE) | (0&amp;lt;&amp;lt;SPIEN) | (1&amp;lt;&amp;lt;WDTON) | &lt;br /&gt;
      (1&amp;lt;&amp;lt;BODLEVEL2) | (1&amp;lt;&amp;lt;BODLEVEL1) | (0&amp;lt;&amp;lt;BODLEVEL0) | (1&amp;lt;&amp;lt;RSTDISBL) );&lt;br /&gt;
unsigned char efuse EFUSE =&lt;br /&gt;
    ((0&amp;lt;&amp;lt;SELFPRGEN));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG: Die Bitpositionen wurden nicht vollständig getestet!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Eine &amp;quot;1&amp;quot; bedeutet hier, dass das Fuse-Bit &#039;&#039;nicht&#039;&#039; programmiert wird - die Funktion also i.A. nicht aktiviert ist. Eine &amp;quot;0&amp;quot; hingegen aktiviert die meisten Funktionen. Dies ist wie im Datenblatt (1 = unprogrammed, 0 = programmed).&lt;br /&gt;
&lt;br /&gt;
Das Makefile muss nun noch um folgende Targets erweitert werden (mit Tabulator einrücken - nicht mit Leerzeichen):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
lfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j lfuses --change-section-address lfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-lfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-lfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U lfuse:w:$(TARGET)-lfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
hfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j hfuses --change-section-address hfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-hfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-hfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U hfuse:w:$(TARGET)-hfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
efuses: build&lt;br /&gt;
        -$(OBJCOPY) -j efuses --change-section-address efuses=0 \&lt;br /&gt;
         -O ihex $(TARGET).elf $(TARGET)-efuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-efuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U efuse:w:$(TARGET)-efuse.hex;&lt;br /&gt;
        fi;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Target &amp;quot;clean&amp;quot; muss noch um die Zeilen&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
        $(REMOVE) $(TARGET)-lfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-hfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-efuse.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
erweitert werden, wenn auch die Fuse-Dateien gelöscht werden sollen.&lt;br /&gt;
&lt;br /&gt;
Um nun die Fusebits des angeschlossenen Controllers zu programmieren muss lediglichein &amp;quot;make lfuses&amp;quot;, &amp;quot;make hfuses&amp;quot; oder &amp;quot;make efuses&amp;quot; gestartet werden.&lt;br /&gt;
Bei den Fuse-Bits ist besondere Vorsicht geboten, da diese das Programmieren des Controllers unmöglich machen können. Also erst programmieren, wenn man einen HV-Programmierer hat oder ein paar Reserve-AVRs zur Hand ;-)&lt;br /&gt;
&lt;br /&gt;
Um weiterhin den &amp;quot;normalen&amp;quot; Flash beschreiben zu können, ist es wichtig, für das Target &amp;quot;*.hex&amp;quot; im Makefile nicht nur &amp;quot;-R .eeprom&amp;quot; als Parameter zu übergeben sondern zusätzlich noch &amp;quot;-R lfuses -R efuses -R hfuses&amp;quot;. Sonst bekommt AVRDUDE Probleme diese Sections in den Flash (wo sie ja nicht hingehören) zu schreiben.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[AVR_Fuses#Vergleich_der_Fuses_bei_verschiedenen_Programmen|Vergleich der Fuses bei verschiedenen Programmen]]&lt;br /&gt;
&lt;br /&gt;
== Externe Referenzspannung des internen Analog-Digital-Wandlers ==&lt;br /&gt;
&lt;br /&gt;
Die minimale (externe) Referenzspannung des ADC darf nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. z.B. beim ATMEGA8 darf sie laut Datenblatt (S.245, Tabelle 103, Zeile &amp;quot;VREF&amp;quot;) 2,0V nicht unterschreiten. HINWEIS: diese Information findet sich erst in der letzten Revision (Rev. 2486O-10/04) des Datenblatts.&lt;br /&gt;
&lt;br /&gt;
Meiner &amp;lt;!-- Wer? - es gibt inzwischen x Leute die mehr oder weniger viel in diesem Artikel geschrieben haben --&amp;gt; eigenen Erfahrung nach kann man aber (auf eigene Gefahr und natürlich nicht für Seriengeräte) durchaus noch ein klein wenig weiter heruntergehen, bei dem von mir unter die Lupe genommenen ATMEGA8L (also die Low-Voltage-Variante) funktioniert der ADC bei 5V Betriebsspannung mit bis zu VREF=1,15V hinunter korrekt, ab 1,1V und darunter digitalisiert er jedoch nur noch Blödsinn). Ich würde sicherheitshalber nicht unter 1,5V gehen und bei niedrigeren Betriebsspannungen mag sich die Untergrenze für VREF am Pin AREF ggf. nach oben&#039;&#039;&#039;(!)&#039;&#039;&#039; verschieben.&lt;br /&gt;
&lt;br /&gt;
In der letzten Revision des Datenblatts ist außerdem korrigiert, dass ADC4 und ADC5 sehr wohl 10 Bit Genauigkeit bieten (und nicht bloß 8 Bit, wie in älteren Revisionen irrtümlich angegeben.)&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* stdio.h, malloc() &lt;br /&gt;
* Code-Optimierungen (&amp;quot;tricks&amp;quot;), siehe auch Application Note [http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf AVR035: Efficient C Coding for AVR]&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
* SPI siehe [http://www.uni-koblenz.de/~physik/informatik/MCU/SPI.pdf SPI Bus mit Atmel AVR]&lt;br /&gt;
* I²C / TWI Bus [http://www.roboternetz.de/wissen/index.php/TWI]&lt;br /&gt;
* Bootloader (bez. auf boot.h)&lt;br /&gt;
* CAN-Bus&lt;br /&gt;
* Einsatz von einfachen Betriebssystemen auf dem AVR&lt;br /&gt;
* Übersicht zu den C bzw. GCC-predefined Makros (__DATE__, __TIME__,...)&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
* ADC ; &lt;br /&gt;
* Timer&lt;br /&gt;
* USB ; Steuerung mit USB&lt;br /&gt;
* Multiplexen Siebensegment&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28256</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28256"/>
		<updated>2008-05-27T17:13:23Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Interruptgesteuerter Programmablauf */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[AVR-GCC]] erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Progammiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong]. Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern, weder in Assembler noch in einer anderen Sprache. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR Controllern finden. Bei WinAVR gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden stetig weiterentwickelt. Erläuterungen und Beispiele beziehen sich auf den C-Compiler avr-gcc ab Version 3.4 und die avr-libc ab Version 1.4.3. Die Unterschiede zu älteren Versionen werden im Haupttext und Anhang zwar erläutert, Anfängern sei jedoch empfohlen, die aktuellen Versionen zu nutzen (für MS-Windows: aktuelle Version des [[WinAVR]]-Pakets). &lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in PDF-Form erhältlich bei:&lt;br /&gt;
http://www.siwawi.arubi.uni-kl.de/avr_projects/AVR-GCC-Tutorial_-_www_mikrocontroller_net.pdf&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels avr-gcc/avr-libc zu erstellen und zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR Controllers, der vom avr-gcc Compiler unterstützt wird (alle ATmegas und die meisten AT90, siehe Dokumentation der avr-libc für unterstützte Typen). Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man [[AVR#Starterkits|hier]].&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc Compiler und die avr-libc. Kostenlos erhältlich für nahezu alle Plattformen und Betriebssysteme. Für MS-Windows im Paket [[WinAVR]]; für Unix/Linux siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]]).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder nur die eigenen C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
&lt;br /&gt;
* Die [[AVR Checkliste]] durcharbeiten.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/2 GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  auf den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
&lt;br /&gt;
* Die Beispieleprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweisen sind aber auch auf C-Programme übertragbar.&lt;br /&gt;
&lt;br /&gt;
* Einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien, um das Problem nachzuvollziehen, sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z. B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.&amp;amp;nbsp;B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.&amp;amp;nbsp;B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen, um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu zwei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer integrierten Entwicklungsumgebung (IDE = &#039;&#039;&#039;I&#039;&#039;&#039;ntegrated &#039;&#039;&#039;D&#039;&#039;&#039;evelopment &#039;&#039;&#039;E&#039;&#039;&#039;nvironment), bei der alle Einstellungen z.&amp;amp;nbsp;B. in Dialogboxen durchgeführt werden können. Unter Anderem kann AVRStudio ab Version 4.12 (kostenlos auf [http://www.atmel.com/ atmel.com]) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs für den avr-gcc (ohne Anspruch auf Vollständigkeit): AtmanAvr C (relativ günstig), KamAVR (kostenlos), VMLab (ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.&amp;amp;nbsp;B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand der universellen und plattformunabhängigen Vorgehensweise mittels make und Makefiles näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind lediglich Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xff;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5a)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/;  // (5b)&lt;br /&gt;
   }                         // (5c)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (6)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In der mit (1) markierten Zeile wird eine so genannte Header-Datei eingebunden. In io.h sind die Registernamen definiert, die im späteren Verlauf genutzt werden.&lt;br /&gt;
&lt;br /&gt;
* Bei (2) beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion main.&lt;br /&gt;
&lt;br /&gt;
* Die Anschlüsse eines AVR (&amp;quot;Beinchen&amp;quot;) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Richtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B zu Ausgängen.&lt;br /&gt;
&lt;br /&gt;
* (4) stellt die Werte der Ausgänge ein. Die den ersten beiden Bits des Ports zugeordneten Anschlüsse (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential).&lt;br /&gt;
&lt;br /&gt;
* (5) ist die so genannte Hauptschleife (main-loop). Dies ist eine Programmschleife, welche kontinuierlich wiederkehrende Befehle enthält. In diesem Beispiel ist sie leer. Der Controller durchläuft die Schleife immer wieder, ohne dass etwas passiert (außer das Strom verbraucht wird). Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Ohne diese Schleife wäre der Zustand des Controllers nach dem Programmende undefiniert.&lt;br /&gt;
&lt;br /&gt;
* (6) wäre das Programmende. Die Zeile ist nur aus Gründen der C-Kompatibilität enthalten: int main(void) besagt, dass die Funktion einen Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein auf dem Controller lauffähiges Programm zu übersetzen, wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen Makefile (ohne Endung) im selben Verzeichnis, in dem auch die Datei main.c mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im folgenden Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\tmp\gcc_tut\quickstart&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei main.hex, in der der Code für den AVR enthalten ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.&amp;amp;nbsp;B. über In-System-Programming (ISP) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann (z.&amp;amp;nbsp;B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio), kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine LED leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode mit einem dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Exkurs: Makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebungen à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Plattformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt, genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.com) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das Makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den im Makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im Makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten Makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt [[AVR-GCC-Tutorial#EEPROM|Speicherzugriffe]]), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendeten Controllers gesetzt. Eine Liste der von avr-gcc und der avr-libc unterstützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien eintragen ==&lt;br /&gt;
&lt;br /&gt;
Der Name der Quellcodedatei, welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.&amp;amp;nbsp;B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
# nicht auskommentiert: EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Das erstellte Makefile und der Code müssen im gleichen Ordner sein, auch sollte der Dateiname nicht verändert werden. &lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem Makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschinencode erzeugt. Beim Update auf eine neue Compilerversion ist zu beachten, dass diese möglicherweise intern andere Optimierungsalgorithmen verwendet und sich dadurch die Größe des Machinencodes etwas ändert, ohne dass man im Quellcode etwas geändert hat.&lt;br /&gt;
&lt;br /&gt;
Als Orientierungswerte die Größe des Maschinencodes bei verschiedenen Optionen für einen nicht näher spezifizierten relativ kleinen Testcode bei Verwendung einer nicht näher spezifizierten Compilerversion. &lt;br /&gt;
&lt;br /&gt;
* -O0 : 12&#039;217 Byte&lt;br /&gt;
&lt;br /&gt;
* -O1 : 9&#039;128 Byte&lt;br /&gt;
&lt;br /&gt;
* -O2 : 1&#039;670 Byte&lt;br /&gt;
&lt;br /&gt;
* -O3 : 3&#039;004 Byte&lt;br /&gt;
&lt;br /&gt;
* -Os : 1&#039;695 Byte&lt;br /&gt;
&lt;br /&gt;
Im diesem Testfall führt die Option -O2 mit zum kompaktesten Code, dies  allerdings hier nur mit 25 Bytes &amp;quot;Vorsprung&amp;quot;. Es kann durchaus sein, dass nur wenige Programmerweiterungen dazu führen, dass Compilieren mit -Os wieder in kompakteren Code resultiert.&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/using_tools.html#gcc_optO avr-libc manual Abschnitt Using the gnu-tools/Compiler-Optionen]&lt;br /&gt;
&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_optflags avr-libc Manual FAQ Nr. 16] (Stand avr-libc Version 1.4.5)&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wird hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (großes S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten, lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Taktfrequenz ===&lt;br /&gt;
&lt;br /&gt;
Neuere Versionen der WinAVR/Mfile Vorlage für Makefiles beinhalten die Definition einer Variablen F_CPU (WinAVR 2/2005). Darin wird die Taktfrequenz des Controllers in Hertz eingetragen. Die Definition steht dann im gesamten Projekt ebenfalls unter der Bezeichnung F_CPU zur Verfügung (z.&amp;amp;nbsp;B. um daraus UART-, SPI- oder ADC-Frequenzeinstellungen abzuleiten).&lt;br /&gt;
&lt;br /&gt;
Die Angabe hat rein &amp;quot;informativen&amp;quot; Charakter, die tatsächliche Taktrate wird über den externen Takt (z.&amp;amp;nbsp;B. Quarz) bzw. die Einstellung des internen R/C-Oszillators  bestimmt. Die Nutzung von F_CPU hat also nur Sinn, wenn die Angabe mit dem tatsächlichen Takt übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Innerhalb neuerer Versionen der avr-libc (ab Version 1.2) wird die Definition der Taktfrequenz (F_CPU) zur Berechnung der Wartefunktionen in delay.h genutzt. Diese funktionieren nur dann korrekt, wenn F_CPU mit der tatsächlichen Taktfrequenz übereinstimmt.&lt;br /&gt;
F_CPU muss dazu jedoch nicht unbedingt im makefile definiert werden. Es reicht aus, wird aber bei mehrfacher Anwendung unübersichtlich, vor &#039;&#039;#include &amp;lt;util/delay.h&amp;gt;&#039;&#039; (veraltet: &#039;&#039;#include &amp;lt;avr/delay.h&amp;gt;&#039;&#039;) ein &#039;&#039;#define F_CPU [hier Takt in Hz]UL&#039;&#039; einzufügen. Bei Nutzung von delay.h ist darauf zu achten, dass die Optimierung des Compilers nicht ausgeschaltet ist, sonst wird sehr viel Code erzeugt und die Wartezeit stimmt nicht mit der gewünschten Zeitspanne überein. Vgl. dazu den [http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html entsprechenden Abschnitt der Dokumentation].&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage so genannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* Im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
* Die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* Weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff: (sollte nur noch in Ausnahmefällen genutzt werden)&lt;br /&gt;
&lt;br /&gt;
* Im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
* Die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* Weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler diese &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, einem Nachbau davon (BootICE, Evertool o.&amp;amp;nbsp;ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
Die Verwendung von Makefiles bietet noch viele weitere Möglichkeiten, einige davon werden im Anhang [[AVR-GCC-Tutorial#Zus.C3.A4tzliche_Funktionen_im_Makefile|Zusätzliche Funktionen im Makefile]] erläutert.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige (Integer) Datentypen =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei stdint.h definiert. &lt;br /&gt;
Zur Nutzung der standardisierten Typen bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// ab avr-libc Version 1.2.0 möglich und empfohlen:&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// veraltet: #include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef signed char        int8_t;&lt;br /&gt;
typedef unsigned char      uint8_t;&lt;br /&gt;
&lt;br /&gt;
typedef short              int16_t;&lt;br /&gt;
typedef unsigned short     uint16_t;&lt;br /&gt;
&lt;br /&gt;
typedef long               int32_t;&lt;br /&gt;
typedef unsigned long      uint32_t;&lt;br /&gt;
&lt;br /&gt;
typedef long long          int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* int8_t steht für einen 8-Bit Integer mit einem Wertebereich -128 bis +127.&lt;br /&gt;
&lt;br /&gt;
* uint8_t steht für einen 8-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 255&lt;br /&gt;
&lt;br /&gt;
* int16_t steht für einen 16-Bit Integer mit einem Wertebereich -32768 bis +32767.&lt;br /&gt;
&lt;br /&gt;
* uint16_t steht für einen 16-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 65535.&lt;br /&gt;
&lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; werden als vorzeichenbehaftete Zahlen abgespeichert. Typen mit vorgestelltem &#039;&#039;u&#039;&#039; dienen der Ablage von postiven Zahlen (inkl. 0). Siehe dazu auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/(Standard) Integer Types.&lt;br /&gt;
&lt;br /&gt;
= Bitfelder =&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bits breit ist.&lt;br /&gt;
&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in eine einzelne Bytevariable zusammenfassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können. Die Rede ist von so genannten Bitfeldern. Diese werden als Strukturelemente definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitfelder sparen Platz im RAM, zu Lasten von Platz im Flash, verschlechtern aber unter Umständen die Les- und Wartbarkeit des Codes. Anfängern wird deshalb geraten, ein &amp;quot;ganzes&amp;quot; Byte (uint8_t) zu nutzen, auch wenn nur ein Bitwert gespeichert werden soll.&lt;br /&gt;
&lt;br /&gt;
Wenn man nur ein paar wenige Variablen vom Typ bool verwenden möchte, kann man&lt;br /&gt;
auch die Headerdatei &amp;lt;stdbool.h&amp;gt; einbinden und sich dann wie gewohnt einen Booltyp anlegen. Variablen dieses Typs brauchen dennoch 1 Byte Speicher, ermöglichen aber eine genaue Unterscheidung zwischen Zahlenvariable und boolscher Variable.&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;C-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher Dinge erledigt werden können, welche nicht zeitkritisch sind. Wenn ein Interrupt ausgelöst wird, so wird automatisch die zugeordnete Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum können für allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden&lt;br /&gt;
AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039;, wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output).  Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt.&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend&lt;br /&gt;
angepasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O-Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek (avr-libc) stellt auch Funktionen zur Abfrage eines einzelnen Bits eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist, wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind &#039;&#039;nicht erforderlich&#039;&#039;, man kann auch &amp;quot;einfache&amp;quot; C-Syntax verwenden, die universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) wenn das Bit gesetzt und 0 (&amp;quot;falsch&amp;quot;) wenn es nicht gesetzt ist. Die Negierung des Ausdrucks, also  &#039;&#039;&#039;!&#039;&#039;&#039;(Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)), entspricht &#039;&#039;bit_is_clear&#039;&#039; und gibt einen Wert &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) zurück, falls das Bit nicht gesetzt ist,&lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O-Register einfach wie eine Variable setzen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und outp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben von Bits ====&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausdruck:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit (für 1) eines Bytes hat die Bitnummer 0, das &amp;quot;höchstwertige&amp;quot; (für 128) die Nummer 7.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );    /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3);   /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die warten, bis ein bestimmter Zustand auf einem Bit erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend gewartet&amp;quot; wird. D.h., der Programmablauf bleibt an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den Watchdog ein, muss man darauf achten, dass dieser auch noch getriggert wird (zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Änderung: Habe PINx und PORTx vertauscht.PINx ist für Pull up und PORTx für high bzw low. Michael.L  - &lt;br /&gt;
&lt;br /&gt;
Was sollte diese Aenderung?? Den Rest des Abschnitts auch gelesen? Evtl. die hier nicht erlaeuterten Zusatzfunktion der neuen AVRs durcheinandergeworfen? Aenderung verworfen bis mehr Details.  m.thomas. &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
// Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
&lt;br /&gt;
DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
/* mehr Tipparbeit aber übersichtlicher: */&lt;br /&gt;
DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&lt;br /&gt;
/* Einige Compiler erlauben die Eingabe von Konstanten im &lt;br /&gt;
   Binärformat, z.B. gcc in WinAVR, NICHT aber aus&lt;br /&gt;
   GNU-gcc-Quellen selbst erstellte Compiler. Diese Schreib-&lt;br /&gt;
   weise also nicht nutzen, wenn Code mit anderen &lt;br /&gt;
   ausgetauscht werden soll. */&lt;br /&gt;
DDRB = 0b00011111;    /* direkte Zuweisung &lt;br /&gt;
                         - übersichtlicher &lt;br /&gt;
                         - nicht standardkonform &lt;br /&gt;
                         - selten portabel&lt;br /&gt;
                         - nur bei modifiziertem GCC */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
Weitere Beispiele:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
DDRB = 0xff; &lt;br /&gt;
// Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; DDB0 );&lt;br /&gt;
// Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( ( 1 &amp;lt;&amp;lt; DDB3 ) | ( 1&amp;lt;&amp;lt;DDB4) );&lt;br /&gt;
// Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB |= ( 1 &amp;lt;&amp;lt; DDB0) | ( 1 &amp;lt;&amp;lt; DDB3 );&lt;br /&gt;
// Alle Pins auf Eingang:&lt;br /&gt;
DDRB = 0x00;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&#039;&#039;&lt;br /&gt;
* &#039;&#039;zuerst die Bits im PORTx-Register setzen und&#039;&#039;&lt;br /&gt;
* &#039;&#039;anschließend die Datenrichtung auf Ausgang stellen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Daraus ergibt sich die Abfolge für einen Eingangspin:&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze PORTx: interner Pull-Up aktiv&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze DDRx: Ausgang &amp;quot;high&amp;quot;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bei der Reihenfolge erst DDRx und dann PORTx, kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;&#039;nicht&#039;&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, &#039;&#039;&#039;sondern&#039;&#039;&#039; Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.&amp;lt;/font&amp;gt; (Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände.)&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch.  Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVRs haben sogar an den meisten Pins softwaremäßig zuschaltbare interne Pull-Up Widerstände, welche wir natürlich auch verwenden können.&lt;br /&gt;
&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;DDC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== (Tasten-)Entprellung ====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim Schließen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&lt;br /&gt;
&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen als mehrfache Impulse gezählt wird. Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* bei alter avr-libc: #include &amp;lt;avr/delay.h&amp;gt; */      &lt;br /&gt;
&lt;br /&gt;
/* Einfache Funktion zum Entprellen eines Tasters */&lt;br /&gt;
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)&lt;br /&gt;
{&lt;br /&gt;
    if ( ! (*port &amp;amp; (1 &amp;lt;&amp;lt; pin)) )&lt;br /&gt;
    {&lt;br /&gt;
        /* Pin wurde auf Masse gezogen, 100ms warten   */&lt;br /&gt;
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz&lt;br /&gt;
        _delay_ms(50); &lt;br /&gt;
        if ( *port &amp;amp; (1 &amp;lt;&amp;lt; pin) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Anwender Zeit zum Loslassen des Tasters geben */&lt;br /&gt;
            _delay_ms(50);&lt;br /&gt;
            _delay_ms(50); &lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; PB0 );                 /* PIN PB0 auf Eingang (Taster)            */&lt;br /&gt;
    PORTB |= ( 1 &amp;lt;&amp;lt; PB0 );                 /* Pullup-Widerstand aktivieren            */&lt;br /&gt;
    ...&lt;br /&gt;
    if (debounce(&amp;amp;PINB, PB0))             /* Falls Taster an PIN PB0 gedrueckt..    */&lt;br /&gt;
        PORTD = PIND ^ ( 1 &amp;lt;&amp;lt; PD7 );  /* ..LED an Port PD7 an-&lt;br /&gt;
                                   bzw. ausschalten */&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesem Beispiel ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritische Anwendungen sollte man ein anderes Verfahren nutzen (z.B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).&lt;br /&gt;
&lt;br /&gt;
Zum Thema Entprellen siehe auch:&lt;br /&gt;
* Artikel [[Entprellung]]&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls ben&amp;amp;ouml;tigt, kann eine 16-Bit Variable auch recht einfach manuell in ihre zwei 8-Bit Bestandteile zerlegt werden. Folgendes Beispiel demonstriert dies anhand des pseudo- 16-Bit Registers UBRR.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
typedef union {&lt;br /&gt;
        uint16_t i16;&lt;br /&gt;
        struct {&lt;br /&gt;
                uint8_t i8l;&lt;br /&gt;
                uint8_t i8h;&lt;br /&gt;
        };&lt;br /&gt;
} convert16to8;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
convert16to8 baud;&lt;br /&gt;
baud.i16 = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
UBRRH = baud.i8h;&lt;br /&gt;
UBRRL = baud.i8l;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/*alternativ:*/&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint16_t wFoo16;&lt;br /&gt;
uint8_t bFooLow, bFooHigh;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
wFoo16   = 0xAA55;                 /* zu &amp;quot;zerlegende&amp;quot; 16Bit-Integer */&lt;br /&gt;
bFooHigh = (uint8_t)(wFoo16 &amp;gt;&amp;gt; 8); /* MS-Byte */&lt;br /&gt;
bFooLow  = (uint8_t)(wFoo16);      /* LS-Byte */&lt;br /&gt;
...&lt;br /&gt;
#define us0(Data) ((unsigned char *)(&amp;amp;Data))&lt;br /&gt;
#define us1(Data) ((unsigned char *)((&amp;amp;Data)+1))&lt;br /&gt;
bFooHigh = us1(wFoo16);&lt;br /&gt;
bFoolow  = us0(wFoo16);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen (z.B. ATmega8) teilen sich UBRRH und UCSRC die gleiche Memory-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL) welches Register tats&amp;amp;auml;chlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und &amp;amp;uuml;bergibt den Wert &#039;&#039;3&#039;&#039; und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und &amp;amp;uuml;bergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Bei einigen 16-bit Registern (insbesondere die der 16-bit Timer) erfolgen Schreibzugriffe über das sogenannte &#039;&#039;temporary Register&#039;&#039;. Die Reihenfolge der Zugriffe bestimmt, wann der Wert tatsächlich ins Register geschrieben wird. Typisch wird erst das High-Byte beschrieben (xxxH) und intern im temporary Register zwischengespeichert. Nachdem das Low-Byte (xxxL) geschrieben wurde, setzt der Controller mit diesem und dem im temporary Register zwischengespeicherten Wert für das High-Byte das 16-bit Register. Dabei ist zu beachten, dass intern nur ein temporary Register verfügbar ist, welches in Interruptroutinen mglw. mit einem anderen Wert überschrieben wird, wenn dort ebenfalls 16-bit Register beschrieben werden. avr-gcc/avr-libc berücksichtigen die korrekte Reihenfolge automatisch, wenn die Register mit ihrem &amp;quot;16-bit Label&amp;quot; (ohne H bzw. L) angesprochen werden, dabei ist der Schutz des temporary Registers vor Überschreiben durch Interruptroutinen dennoch zu beachten (im Zweifel beim Schreibzugriff die Interrupts kurzzeitig global deaktivieren).&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t key_pressed(const volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if ( last_state == ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) ) ) {&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    i = key_pressed( &amp;amp;PINB, PB1 );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Im Übrigen können mit volatile gekennzeichnete Variablen auch als const deklariert werden, um sicherzustellen, dass sie nur noch von der Hardware änderbar sind.&lt;br /&gt;
&lt;br /&gt;
= Der UART =&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Über den [[UART]] kann ein AVR leicht mit einer [[RS-232]]-Schnittstelle eines PC oder sonstiger Geräte mit &amp;quot;serieller Schnittstelle&amp;quot; verbunden werden. &lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungen des UART:&lt;br /&gt;
&lt;br /&gt;
* Debug-Schnittstelle: z.B. zur Anzeige von Zwischenergebnissen (&amp;quot;printf-debugging&amp;quot; - hier besser &amp;quot;UART-debugging&amp;quot;) auf einem PC. Auf dem Rechner reicht dazu ein [[RS-232#Terminalprogramme|Terminalprogramm]] (MS-Windows: Hyperterm oder besser Bray-Terminal, [http://www.mikrocontroller.net/forum/read-8-155472.html#new HTerm]; Unix/Linux z.B. minicom). Ein direkter Anschluss ist aufgrund unterschiedlicher Pegel nicht möglich, jedoch sind entsprechende Schnittstellen-ICs wie z.B. ein MAX232 günstig und leicht zu integrieren. Rechner ohne serielle Schnittstelle können über fertige USB-seriell-Adapter angeschlossen werden. &lt;br /&gt;
* &amp;quot;Mensch-Maschine Schnittstelle&amp;quot;: z.B. Konfiguration und Statusabfrage über eine &amp;quot;Kommandozeile&amp;quot; oder Menüs (siehe z.B. Forumsbeitrag [http://www.mikrocontroller.net/topic/52985 Auswertung RS232-Befehle]) &lt;br /&gt;
* Übertragen von gespeicherten Werten: z.B. bei einem Datenlogger&lt;br /&gt;
* Anschluss von Geräten mit serieller Schnittstelle (z.B. (Funk-)Modems, Mobiltelefone, Drucker, Sensoren, &amp;quot;intelligente&amp;quot; LC-Displays, GPS-Empfänger). &lt;br /&gt;
* &amp;quot;Feldbusse&amp;quot; auf RS485/RS422-Basis mittels entsprechenden Bustreiberbausteinen (z.B. MAX485)&lt;br /&gt;
* DMX, Midi etc.&lt;br /&gt;
* LIN-Bus (&#039;&#039;&#039;L&#039;&#039;&#039;ocal &#039;&#039;&#039;I&#039;&#039;&#039;nterconnect &#039;&#039;&#039;N&#039;&#039;&#039;etwork): Preiswerte Sensoren/Aktoren in der Automobiltechnik und darüber hinaus&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben ein bis zwei vollduplexfähigen UART (&#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) schon eingebaut (&amp;quot;Hardware-UART&amp;quot;). &lt;br /&gt;
Übrigens: Vollduplex heißt nichts anderes, als dass der Baustein gleichzeitig senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega, ATtiny) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterscheidet sich vom UART hauptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurationsregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit zeigt an, ob der Sendepuffer bereit ist, um ein zu sendendes Zeichen aufzunehmen. Das Bit wird vom AVR gesetzt (1), wenn der Sendepuffer leer ist. Es wird gelöscht (0), wenn ein Zeichen im Sendedatenregister vorhanden ist und noch nicht in das Sendeschieberegister übernommen wurde. Atmel empfiehlt aus Kompatibilitätsgründen mit kommenden µC, UDRE auf 0 zu setzen, wenn das UCSRA Register beschrieben wird.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
\mathrm{UBRR} = \frac{\mathrm{Taktfrequenz}}{\mathrm{Baudrate} \cdot 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS-232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. &lt;br /&gt;
&amp;lt;!-- &amp;quot;Hackerloesung&amp;quot; auskommentiert - nicht so gut in einem &amp;quot;Einsteiger-Tutorial&amp;quot; - mthomas&lt;br /&gt;
Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfiehlt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== UART initialisieren ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben. Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach gewünschter Funktionsweise die benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Da wir vorerst nur senden möchten und noch keine Interrupts auswerten wollen, gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs mit USART haben mehrere Konfigurationsregister und erfordern eine etwas andere Konfiguration. Für einen ATmega16 z.B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                 // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);     // Asynchron 8N1&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist noch das Baudratenregister &#039;&#039;&#039;UBRR&#039;&#039;&#039; einzustellen. Bei neueren AVRs besteht es aus zwei Registern &#039;&#039;&#039;UBRRL&#039;&#039;&#039; und &#039;&#039;&#039;UBRRH&#039;&#039;&#039;. Der Wert dafür ergibt sich aus der angegebenen Formel durch Einsetzen der Taktfrequenz und der gewünschten Übertragungsrate. Das Berechnen der Formel wird dem [[C-Präprozessor|Präprozessor]] überlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann&lt;br /&gt;
   F_CPU im Makefile definiert werden, eine nochmalige Definition&lt;br /&gt;
   hier wuerde zu einer Compilerwarnung fuehren. Daher &amp;quot;Schutz&amp;quot; durch&lt;br /&gt;
   #ifndef/#endif &lt;br /&gt;
&lt;br /&gt;
   Dieser &amp;quot;Schutz&amp;quot; kann zu Debugsessions führen, wenn AVRStudio &lt;br /&gt;
   verwendet wird und dort eine andere, nicht zur Hardware passende &lt;br /&gt;
   Taktrate eingestellt ist: Dann wird die folgende Definition &lt;br /&gt;
   nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) &lt;br /&gt;
   von AVRStudio - daher Ausgabe einer Warnung falls F_CPU&lt;br /&gt;
   noch nicht definiert: */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000&amp;quot;&lt;br /&gt;
#define F_CPU 4000000UL    // Systemtakt in Hz &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600UL          // Baudrate&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) // Fehler in Promille, 1000 = kein Fehler.&lt;br /&gt;
&lt;br /&gt;
#if ((BAUD_ERROR&amp;lt;990) || (BAUD_ERROR&amp;gt;1010))&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;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    UBRR = UBRR_VAL;&lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&amp;lt;!-- mthomas: warum nicht UL?, wird von AVRStudio auch mit UL übergeben --&amp;gt;&lt;br /&gt;
Wieder für den Mega16 mit zwei Registern für die Baudrateneinstellung eine etwas andere Programmierung. Wichtig ist, dass UBRRH &#039;&#039;&#039;vor&#039;&#039;&#039; UBRRL geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmega16 */&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einige AVR (z.B. ATmega169, ATmega48/88/168, AT90CAN jedoch nicht für z.B. ATmega16/32, ATmega128, ATtiny2313) wird durch die Registerdefinitionen der avr-libc (io*.h) auch für Controller mit zwei UBRR-Registern (UBRRL/UBRRH) ein UBRR bzw. UBRR0 als &amp;quot;16-bit-Register&amp;quot; definiert und man kann auch Werte direkt per UBRR = UBRR_VAL zuweisen. Intern werden dann zwei Zuweisungen für UBRRH und UBRRL generiert. Dies ist nicht bei allen Controllern möglich, da die beiden Register nicht bei allen aufeinanderfolgende Addressen aufweisen. Die getrennte Zuweisung an UBRRH und UBRRL wie im Beispiel gezeigt ist universeller und portabler und daher vorzuziehen.&lt;br /&gt;
&lt;br /&gt;
Die Makros sind sehr praktisch, da sie sowohl automatisch den Wert für UBRR als auch den Fehler in der generierten Baudrate berechnen und im Falle einer Überschreitung (+/-1%) einen Fehler und somit Abbruch im Compilerablauf generieren. Damit können viele Probleme mit &amp;quot;UART sendet komische Zeichen&amp;quot; vermieden werden. Ausserdem kann man mühelos die Einstellung an eine neue Taktfrequenz bzw. Baudrate anpassen, ohne selber rechnen oder in Tabellen nachschlagen zu müssen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. Vorher ist zu prüfen, ob das UART-Modul bereit ist, das zu sendende Zeichen entgegenzunehmen. Die Bezeichnungen des/der Statusregisters mit dem Bit UDRE ist abhängig vom Controllertypen (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    // bei AVR mit einem UART (&amp;quot;classic AVR&amp;quot; z.B. AT90S8515)&lt;br /&gt;
    while (!(USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                  /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&lt;br /&gt;
    /** ODER **/&lt;br /&gt;
&lt;br /&gt;
    // bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                    /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe &amp;quot;String senden&amp;quot; wird durch zwei Funktionen abgearbeitet. Die universelle/controllerunabhängige Funktion uart_puts übergibt jeweils ein Zeichen der Zeichenkette an eine Funktion uart_putc, die abhängig von der vorhandenen Hardware implementiert werden muss. In der Funktion zum Senden eines Zeichens ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// putc fuer AVR mit einem UART (z.B. AT90S8515)&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while(!(USR &amp;amp; (1 &amp;lt;&amp;lt; UDRE)))  /* warte, bis UDR bereit */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = c;                     /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** ODER **/&lt;br /&gt;
&lt;br /&gt;
// bei neueren AVRs andere Bezeichnung fuer die Statusregister, hier ATmega16:&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich */&lt;br /&gt;
    {&lt;br /&gt;
    }                             &lt;br /&gt;
&lt;br /&gt;
    UDR = c;                      /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* puts ist unabhaengig vom Controllertyp */&lt;br /&gt;
void uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != &#039;\0&#039; also ungleich dem &amp;quot;String-Endezeichen&amp;quot; */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die in uart_putc verwendeten Schleifen, in denen gewartet wird bis die UART-Hardware zum senden bereit ist, sind insofern etwas kritisch, da während des Sendens eines Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. empfangenen Zeichen/Bytes zwischengespeichert und in Interruptroutinen an die U(S)ART-Hardware weitergegeben bzw. von ihr ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&lt;br /&gt;
&lt;br /&gt;
=== Schreiben von Variableninhalten ===&lt;br /&gt;
&lt;br /&gt;
Sollen Inhalte von Variablen (Ganzzahlen, Fließkomma) in &amp;quot;menschenlesbarer&amp;quot; Form gesendet werden, ist vor dem Transfer eine Umwandlung in Zeichen (&amp;quot;ASCII&amp;quot;) erforderlich. Bei nur einer Ziffer ist diese Umwandlung relativ einfach: man addiert den ASCII-Wert von Null zur Ziffer und kann diesen Wert direkt senden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_putc (s.o.)&lt;br /&gt;
&lt;br /&gt;
// Ausgabe von 0123456789&lt;br /&gt;
char c;&lt;br /&gt;
&lt;br /&gt;
for (uint8_t i=0; i&amp;lt;=9; ++i) {&lt;br /&gt;
    c = i + &#039;0&#039;;&lt;br /&gt;
    uart_putc( c );&lt;br /&gt;
    // verkuerzt: uart_putc( i + &#039;0&#039; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll mehr als eine Ziffer ausgegeben werden, bedient man sich zweckmäßigerweise vorhandener Funktionen zur Umwandlung von Zahlen in Zeichenketten/Strings. Die Funktion der avr-libc zur Umwandlung von vorzeichenbehafteten 16bit-Ganzzahlen (int16_t) in Zeichenketten heißt &#039;&#039;itoa&#039;&#039; (Integer to ASCII). Man muss der Funktion einen Speicherbereich zur Verarbeitung (buffer) mit Platz für alle Ziffern, das String-Endezeichen (&#039;\0&#039;) und evtl. das Vorzeichen bereitstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   char s[7];&lt;br /&gt;
   int16_t i = -12345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   itoa( i, s, 10 ); // 10 fuer radix -&amp;gt; Dezimalsystem&lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // da itoa einen Zeiger auf den Beginn von s zurueckgibt verkuerzt auch:&lt;br /&gt;
   uart_puts( itoa( i, s, 10 ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für vorzeichenlose 16bit-Ganzzahlen (uint16_t) exisitert &#039;&#039;utoa&#039;&#039;. Die Funktionen für 32bit-Ganzzahlen (int32_t und uint32_t) heißen &#039;&#039;ltoa&#039;&#039; bzw. &#039;&#039;ultoa&#039;&#039;. Da 32bit-Ganzzahlen mehr Stellen aufweisen können, ist ein entsprechend größerer Pufferspeicher vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Auch Fließkommazahlen (float/double) können mit breits vorhandenen Funktionen in Zeichenfolgen umgewandelt werden, dazu existieren die Funktionen &#039;&#039;dtostre&#039;&#039; und &#039;&#039;dtostrf&#039;&#039;. dtostre nutzt Exponentialschreibweise (&amp;quot;engineering&amp;quot;-Format). (Hinweis: z.Zt. existiert im avr-gcc kein &amp;quot;echtes&amp;quot; double, intern wird immer mit &amp;quot;einfacher Genauigkeit&amp;quot;, entsprechend float, gerechnet.) &lt;br /&gt;
&lt;br /&gt;
dtostrf und dtostre benötigen die libm.a der avr-libc. Bei Nutzung von Makefiles ist der Parameter -lm in in LDFLAGS anzugeben (Standard in den WinAVR/mfile-Makefilevorlagen). Nutzt man AVRStudio als IDE für den GNU-Compiler (gcc-Plugin) ist die libm.a unter Libaries auszuwählen: Project -&amp;gt; Configurations Options -&amp;gt; Libaries -&amp;gt; libm.a mit dem Pfeil nach rechts einbinden. Siehe auch die [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|FAQ]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
/* lt. avr-libc Dokumentation:&lt;br /&gt;
char* dtostrf(&lt;br /&gt;
  double __val,&lt;br /&gt;
  char   __width,&lt;br /&gt;
  char   __prec,&lt;br /&gt;
  char * __s&lt;br /&gt;
)  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   // Pufferspeicher ausreichend groß&lt;br /&gt;
   // evtl. Vorzeichen + width + Endezeichen:&lt;br /&gt;
   char s[8]; &lt;br /&gt;
   float f = -12.345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   dtostrf( f, 6, 3, s ); &lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // verkürzt: uart_puts( dtostrf( f, 7, 3, s ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfangen ==&lt;br /&gt;
=== einzelne Zeichen empfangen ===&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von Zeichen muss der Empfangsteil des UART bei der Initialisierung aktiviert werden, indem das RXEN-Bit im jeweiligen Konfigurationsregister (UCSRB bzw UCSR0B/UCSR1B) gesetzt wird. Im einfachsten Fall wird solange gewartet, bis ein Zeichen empfangen wurde, dieses steht dann im UART-Datenregister (UDR bzw. UDR0 und UDR1 bei AVRs mit 2 UARTS) zur Verfügung (sogen. &amp;quot;Polling-Betrieb&amp;quot;). Ein Beispiel für den ATmega16:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Zusaetzlich zur Baudrateneinstellung und der weiteren Initialisierung: */&lt;br /&gt;
void Usart_EnableRX()&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= ( 1 &amp;lt;&amp;lt; RXEN );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion blockiert den Programmablauf. Alternativ kann das RXC-Bit in einer Programmschleife abgefragt werden und dann nur bei gesetztem RXC-Bit UDR ausgelesen werden. Eleganter und in den meisten Anwendungsfällen &amp;quot;stabiler&amp;quot; ist die Vorgehensweise, die empfangenen Zeichen in einer Interrupt-Routine einzulesen und zur späteren Verarbeitung in einem Eingangsbuffer (FIFO-Buffer) zwischenzuspeichern. Dazu existieren fertige und gut getestete [[Libraries|Bibliotheken]] &amp;lt;!-- &amp;quot;echte Libraries&amp;quot; (.a) wie im Verweis beschrieben sind hier eigentlich nicht gemeint, verwirrt hier etwas, da AVR-&amp;quot;Libraries&amp;quot; meist per #defines anpassbare Source-Codes sind, vielleicht so: --&amp;gt; und Quellcodekomponenten (z.B. UART-Library von P. Fleury, procyon-avrlib und einige in der &amp;quot;Academy&amp;quot; von avrfreaks.net).&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html Dokumenation der avr-libc/stdlib.h]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_printf Die Nutzung von printf]&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&amp;lt;!-- nimmermehr: * siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (printf etc.) im [[AVR-Tutorial:_UART]]. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: 9bit&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Zeichenketten (Strings) ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Zeichenketten, muß man sich zunächst darüber im klaren sein, daß es ein Kriterium geben muß, an dem der µC erkennen kann, wann ein String zu Ende ist. Sehr oft wird dazu das Zeichen &#039;Return&#039; benutzt, um das Ende eines Strings zu markieren. Dies ist vom Benutzer einfach eingebbar und er ist auch daran gewöhnt, daß er eine Eingabezeile mit einem Druck auf die Return Taste abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Prinzipiell gibt es jedoch keine Einschränkung bezüglich dieses speziellen Zeichens. Es muß nur sichergestellt werden, daß dieses spezielle &#039;Ende eines Strings&#039; - Zeichen nicht mit einem im Text vorkommenden Zeichen verwechselt werden kann. Wenn also im zu übertragenden Text beispielsweise kein &#039;;&#039; vorkommt, dann spricht nichts dagegen, einen String mit einem &#039;;&#039; abschließen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird die durchaus übliche Annahme getroffen, daß eine Stringübertragung identisch ist mit der Übertragung einer Textzeile und daher mit einem Return (&#039;\n&#039;) abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Das Problem der Übertragung eines Strings reduziert sich damit auf die Aufgabenstellung: Empfange und Sammle Zeichen in einem char Array, bis entweder das Array voll ist oder das &#039;String Ende Zeichen&#039; empfangen wurde. Danach wird der empfangene Text noch mit einem &#039;\0&#039; Zeichen abgeschlossen um einen Standard C-String daraus zu machen, mit dem dann weiter gearbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_gets( char* Buffer, uint8_t MaxLen )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t NextChar;&lt;br /&gt;
  uint8_t StringLen = 0;&lt;br /&gt;
&lt;br /&gt;
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen&lt;br /&gt;
&lt;br /&gt;
                                  // Sammle solange Zeichen, bis:&lt;br /&gt;
                                  // * entweder das String Ende Zeichen kam&lt;br /&gt;
                                  // * oder das aufnehmende Array voll ist&lt;br /&gt;
  while( NextChar != &#039;\n&#039; &amp;amp;&amp;amp; StringLen &amp;lt; MaxLen - 1 ) {&lt;br /&gt;
    *Buffer++ = NextChar;&lt;br /&gt;
    StringLen++;&lt;br /&gt;
    NextChar = uart_getc();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
                                  // Noch ein &#039;\0&#039; anhängen um einen Standard&lt;br /&gt;
                                  // C-String daraus zu machen&lt;br /&gt;
  *Buffer = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf ist darauf zu achten, dass das empfangende Array auch mit einer&lt;br /&gt;
vernünftigen Größe definiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
  char Line[40];      // String mit maximal 39 zeichen&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) );&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Interruptbetrieb==&lt;br /&gt;
&lt;br /&gt;
Beim ATMEGA8 ist es möglich, dass RXCIE Bit im Register UCSRB zu setzen.&lt;br /&gt;
Dieser Interrupt wird immer ausglöst, wenn Daten erfolgreich empfangen wurden.&lt;br /&gt;
Zusätzlich braucht man die Routine:&lt;br /&gt;
&lt;br /&gt;
ISR (USART_RXC_vect) {&lt;br /&gt;
   //irgendein Code&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
natürlich muss &amp;quot;Global Interrupt Enable&amp;quot; Aktiviert sein.&lt;br /&gt;
!! Nur getestet beim ATMEGA8 !!&lt;br /&gt;
&lt;br /&gt;
[BAUSTELLE! Aus Lerngründen eventuell als eigenen UART-Interrupt-Block hinter den grundlegenden Interrupt-Teil im Tutorial und hier eine kurze Einführung und einen Verweis darauf anbieten.]&lt;br /&gt;
&lt;br /&gt;
* Unterschied Polling-Betrieb (bisher, oben) und Interrupt-Betrieb&lt;br /&gt;
* Empfangen (Receive)&lt;br /&gt;
** Verändertes UART-Init, ISR (RXC), ggf. Fallstricke ([http://www.mikrocontroller.net/topic/84256#707214 UDR in der ISR lesen!]), Philosophie einer ISR (kurz und schmerzlos), Datenaustausch ISR zu Restprogramm (volatile)&lt;br /&gt;
** Einfachstbeispiel ([http://www.mikrocontroller.net/topic/84228#707052 Echo] (noch buggy beim Datenzugriff, siehe Lit. 2+3!)), ggf. LED zur ISR-Empfangsanzeige oder Overflow-Anzeige&lt;br /&gt;
* FIFO-Puffer, Ringpuffer&lt;br /&gt;
* Senden (Transmit)&lt;br /&gt;
** Variante &amp;quot;UART Data Register Empty&amp;quot; (UDRE) &lt;br /&gt;
** Variante &amp;quot;UART Transmit Complete&amp;quot; (TXC) &lt;br /&gt;
* Fertige UART-Bibliotheken (-&amp;gt;Fleury, -&amp;gt;Procyon)&lt;br /&gt;
* Literatur: &lt;br /&gt;
** 1/ [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=48188 avrfreaks.net Tutorial] inkl. Diskussion (engl.)&lt;br /&gt;
** 2/ avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_16bitio Why do some 16-bit timer registers sometimes get trashed?]&lt;br /&gt;
** 3/ [[Interrupt]] und atomarer Datenzugriff&lt;br /&gt;
&lt;br /&gt;
==Software-UART==&lt;br /&gt;
&lt;br /&gt;
Falls die Zahl der vorhandenen Hardware-UARTs nicht ausreicht, können weitere Schnittstellen über sogennante Software-UARTs ergänzt werden. Es gibt dazu (mindestens) zwei Ansätze: &lt;br /&gt;
* Der bei AVRs üblichste Ansatz basiert auf dem Prinzip, dass ein externer Interrupt-Pin für den Empfang (&amp;quot;RX&amp;quot;) genutzt wird. Das Startbit löst den Interrupt aus, in der Interrupt-Routine (ISR) wird der externe Interrupt deaktiviert und ein Timer aktiviert. In der Interrupt-Routine des Timers wird der Zustand des Empfangs-Pins entsprechend der Baudrate abgetastet. Nach Empfang des Stop-Bits wird der externe Interrupt wieder aktiviert. Senden kann über einen beliebigen Pin (&amp;quot;TX&amp;quot;) erfolgen, der entsprechend der Baudrate und dem zu sendenden Zeichen auf 0 oder 1 gesetzt wird. Die Implementierung ist nicht ganz einfach, es existieren dazu aber fertige Bibliotheken (z.B. bei [http://www.avrfreaks.net/ avrfreaks] oder in der [http://hubbard.engr.scu.edu/embedded/avr/avrlib/ Procyon avrlib]).&lt;br /&gt;
* Ein weiterer Ansatz erfordert keinen Pin mit &amp;quot;Interrupt-Funktion&amp;quot; aber benötigt mehr Rechenzeit. Jeder Input-Pin kann als Empfangspin (RX) dienen. Über einen Timer wird der Zustand des RX-Pins mit einem vielfachen der Baudrate abgetastet (dreifach scheint üblich) und High- bzw. Lowbits anhand einer Mindestanzahl identifiziert. (Beispiel: &amp;quot;Generic Software Uart&amp;quot; Application-Note von IAR)&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (z.B. ATtiny26 oder ATmega48,88,168,169) verfügen über ein Universal Serial Interface (USI), das teilweise UART-Funktion übernehmen kann. Atmel stellt eine Application-Note bereit, in der die Nutzung des USI als UART erläutert wird (im Prinzip &amp;quot;Hardware-unterstützter Software-UART&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==Handshaking==&lt;br /&gt;
Wenn der Sender ständig sendet, wird irgendwann der Fall eintreten, daß der Empfänger nicht bereit ist, neue Zeichen zu empfangen. In diesem Fall muß durch ein &#039;&#039;&#039;Handshake-Verfahren&#039;&#039;&#039; die Situation bereinigt werden. Handshake bedeutet nichts anderes, als daß der Empfänger dem Sender mitteilt, daß er zur Zeit keine Daten annehmen kann und der Sender die Übertragung der nächsten Zeichen solange einstellen soll, bis der Empfänger signalisiert, daß er wieder Zeichen aufnehmen kann.&lt;br /&gt;
===Hardwarehandshake (RTS/CTS)===&lt;br /&gt;
Beim Hardwarehandshake werden zusätzlich zu den beiden Daten-Übertragungsleitungen noch 2 weitere Leitungen benötigt: &#039;&#039;&#039;RTS&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;equest &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end) und &#039;&#039;&#039;CTS&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end). Jeder der beiden Kommunikationspartner ist verpflichtet, bevor ein Zeichen gesendet wird, den Zustand der &#039;&#039;&#039;RTS&#039;&#039;&#039; Leitung zu überprüfen. Nur wenn die Gegenstelle darauf Empfangsbereitschaft signalisiert, darf das Zeichen gesendet werden. Um der Gegenstelle zu signalisieren, daß sie zur Zeit keine Zeichen schicken soll, wird die Leitung &#039;&#039;&#039;CTS&#039;&#039;&#039; benutzt.&lt;br /&gt;
&lt;br /&gt;
===Softwarehandshake (XON/XOFF)===&lt;br /&gt;
Beim Softwarehandshake sind keine speziellen Leitungen notwendig. Statt dessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, daß Senden einzustellen bzw. wieder aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;XOFF&#039;&#039;&#039; Aufforderung das Senden einzustellen&lt;br /&gt;
* &#039;&#039;&#039;XON&#039;&#039;&#039;  Gegenstelle darf wieder senden&lt;br /&gt;
&lt;br /&gt;
Nachteilig bei einem Softwarehandshake ist es, daß dadurch keine direkte binäre Datenübertragung mehr möglich ist. Von den möglichen 256 Bytewerten werden ja 2 (nämlich &#039;&#039;&#039;XON&#039;&#039;&#039; und &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus.&lt;br /&gt;
&lt;br /&gt;
==Fehlersuche==&lt;br /&gt;
Erstaunlich oft wird im Forum der Hilferuf laut: &amp;quot;Meine UART funktioniert nicht, was mache ich falsch&amp;quot;. In der überwiegenden Mehrzahl der Fälle stellt sich dann heraus, daß es sich um ein Hardwareproblem handelt, wobei da wiederrum der Löwenanteil auf das Konto einer nicht korrekt eingestellten Taktrate geht: Der µC benutzt nicht einen angeschlossenen Quarz, so wie er auch im Programm eingetragen ist, sondern läuft immer noch mit dem internen RC-Takt. Daraus resultiert aber auch, daß der Baudraten Konfigurationswert falsch berechnet wird.&lt;br /&gt;
&lt;br /&gt;
Eine Checkliste zum Aufspüren solcher Fehler findet sich [[AVR_Checkliste#UART.2FUSART|hier]].&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Analoge Eingangswerte werden in der Regel über den AVR Analog-Digital-Converter (AD-Wandler, ADC) eingelesen, der in vielen Typen verfügbar ist (typisch 10bit Auflösung). Durch diesen werden analogen Signale (Spannungen) in digitale Zahlenwerte gewandelt. Bei AVRs, die über keinen internen AD-Wandler verfügen (z.B. ATmega162, ATtiny2313), kann durch externe Beschaltung (R/C-Netzwerk und &amp;quot;Zeitmessung&amp;quot;) die Funktion des AD-Wandlers &amp;quot;emuliert&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
Es existieren keine AVRs mit eingebautem Digital-Analog-Konverter (DAC). Diese Funktion muss durch externe Komponenten nachgebildet werden (z.B. PWM und &amp;quot;Glättung&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Unabhängig davon besteht natürlich immer die Möglichkeit, spezielle Bausteine zur Analog-Digital- bzw. Digital-Analog-Wandlung zu nutzen und diese über eine digitale Schnittstelle (z.b. SPI oder I2C) mit einem AVR anzusteuern.&lt;br /&gt;
&lt;br /&gt;
== AC (Analog Comparator) ==&lt;br /&gt;
&lt;br /&gt;
Der Comparator vergleicht 2 Spannungen an den Pins AIN0 und AIN1 und gibt einen Status aus welche der beiden Spannungen größer ist. AIN0 Dient dabei als Referenzspannung (Sollwert) und AIN1 als Vergleichsspannung (Istwert). Als Referenzspannung kann auch alternativ eine interne Referenzspannung ausgewählt werden.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Liegt die Vergleichsspannung (IST) unter der der Referenzspannung (SOLL) gibt der Comperator eine logische 0 aus. ISt die Vergleichsspannung hingegen größer als die Referenzspannung wird eine logische 1 ausgegeben.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Der Comperator arbeitet völlig autark bzw. parallel zum Prozessor. Für mobile Anwendungen empfiehlt es sich ihn abzuschalten sofern er nicht benötigt wird, da er ansonsten Strom benötigt. Der Comparator kann Interruptgesteuert abgefragt werden oder im Pollingbetrieb.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Steuer- bzw. Statusregister ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ACSR (0x28) - Analog Comparator Status Register&amp;lt;BR&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACD&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;AINBG&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACO&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACI&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | n/a&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 7 ACD - Analog Comperator Disable: 0 = Comparator ein, 1 = Comparator aus. Wird dieses Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 ACIE ggf. abgeschaltet werden.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 6 AINBG - Analog Comparator Bandgap Select: Ermöglicht das umschalten zwischen interner und externer Referenzspannung. 1 = interne (~1,3 Volt), 0 = externe Referenzspannung (an Pin AIN0)&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 5 ACO - Analog Coparator Output: Hier wird das Ergebnis des Vergleichs angezeigt. Es liegt typischerweise nach 1-2 Taktzyklen vor. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ergebnis: &amp;lt;BR&amp;gt;IST &amp;lt; SOLL --&amp;gt; 0&amp;lt;BR&amp;gt;&lt;br /&gt;
IST &amp;gt; SOLL --&amp;gt; 1&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4 ACI - Analog Comparator Interrupt Flag: Dieses Bit wird von der Hardware gesetzt wenn ein Interruptereignis das in Bit 0 und 1 definiert ist eintritt. Dieses Bit löst noch keinen Interrupt aus! Die Interruptroutine wird nur dann ausgeführt wenn das Bit 3 ACIE gesetzt ist und global Interrupts erlaubt sind (I-Bit in SREG=1). Das Bit 4 ACI wird wieder gelöscht wenn die Interruptroutine ausgeführt wurde oder wenn manuell das Bit auf 1 gesetzt wird. Das Bit kann für Abfragen genutzt werden, steuert oder konfuguriert aber nicht den Comparator.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 3 ACIE - Analog Comparator Interrupt Enable: Ist das Bit auf 1 gesetzt wird immer ein Interrupt ausgelöst wenn das Ereignis das in Bit 1 und 0 definiert ist eintritt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 2 ACIC - Analog Comparator Input Capture Enable: Wird das Bit gesetzt wird der Comparatorausgang intern mit dem Counter 1 verbunden. Es könnten damit z.b. die Anzahl der Vergleiche im Counter1 gezählt werden. Um den Comparator an den Timer1 Input Capture Interrupt zu verbinden muß im Timerregister das TICIE1 Bit auf 1 gesetzt werden. Der Trigger wird immer dann ausgelöst wenn das in Bit 1 und 0 definierte Ereignis eintritt.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 1,0 ACIS1,ACIS0 - Analog Comparator Interrupt select: Hier wird definiert welche Ereignisse einen Interrupt auslösen sollen:&amp;lt;BR&amp;gt;&lt;br /&gt;
00 = Interrupt auslösen bei jedem Flankenwechsel&amp;lt;BR&amp;gt;&lt;br /&gt;
10 = Interrupt auslösen bei fallender Flanke&amp;lt;BR&amp;gt;&lt;br /&gt;
11 = Interrupt auslösen bei steigender Flanke&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Werden diese Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 gelöscht werden.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der Analog-Digital-Konverter (ADC) wandelt analoge Signale in digitale Werte um, welche vom Controller interpretiert werden können. Einige AVR-Typen haben bereits einen mehrkanaligen Analog-Digital-Konverter eingebaut. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst werden kann, wird durch die Auflösung des ADC in Anzahl Bits angegeben, man hört bzw. liest jeweils von 8-Bit-ADC oder 10-Bit-ADC oder noch höher. ADCs die in AVRs enthalten sind haben zur Zeit eine maximale Auflösung von 10-Bit.&lt;br /&gt;
&lt;br /&gt;
Ein ADC mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des Analog-Digital-Konverters ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Der interne ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem Analog-Digital-Wandler (ADC) zurückgreifen, die über mehrere Kanäle verfügen. Kanäle heißt in diesem Zusammenhang, dass zwar bis zu zehn analoge Eingänge am AVR verfügbar sind, aber nur ein &amp;quot;echter&amp;quot; Analog-Digital-Wandler zur Verfügung steht, vor der eigentlichen Messung ist also einzustellen, welcher Kanal (&amp;quot;Pin&amp;quot;) mit dem Wandler verbunden und gemessen wird.&lt;br /&gt;
&lt;br /&gt;
Die Umwandlung innerhalb des AVR basiert auf der schrittweisen Näherung. Beim AVR müssen die Pins &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AVCC&#039;&#039;&#039; beschaltet werden. Für genaue Messungen sollte AVCC über ein L-C Netzwerk mit VCC verbunden werden, um Spannungsspitzen und -einbrüche vom Analog-Digital-Wandler fernzuhalten. Im Datenblatt findet sich dazu eine Schaltung, die 10uH und 100nF vorsieht.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Analog-Digital-Wandlung wird auf eine Referenzspannung bezogen. Aktuelle AVRs bieten drei Möglichkeiten zur Wahl dieser Spannung:&lt;br /&gt;
&lt;br /&gt;
* Eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; am Anschlusspin &#039;&#039;&#039;AREF&#039;&#039;&#039;. Die minimale (externe) Referenzspannung darf jedoch nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. &lt;br /&gt;
&lt;br /&gt;
* Verfügt der AVR über eine interne Referenzspannung, kann diese genutzt werden. Alle aktuellen AVRs mit internem AD-Wandler sollten damit ausgestattet sein (vgl. Datenblatt: 2,56V oder 1,1V je nach Typ). Das Datenblatt gibt auch über die Genauigkeit dieser Spannung Auskunft.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;interne&#039;&#039;&#039; Referenzspannung wird auf &#039;&#039;&#039;Vcc&#039;&#039;&#039; bezogen, eine externe Referenzspannung auf &#039;&#039;&#039;GND (Masse)&#039;&#039;&#039;. Davon unabhängig werden digitalisierte Spannungen &#039;&#039;&#039;immer&#039;&#039;&#039; auf GND bezogen. Beim ATMEGA8 z.B. ist der Pin AREF über 32kOhm mit GND verbunden, d.h. man muss diese doch extrem niedrige Eingangsimpedanz mit in die Berechnung für einen Spannungsteiler einbeziehen, bzw. kann diesen Widerstand als R2 gleich mit benutzen. Formel für Spannungsteiler: Udiv = U / ((R1 + R2) / R2)&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung von Vcc oder der internen Referenz wird empfohlen, einen Kondensator zwischen dem AREF-Pin und GND anzuordnen. Die Festlegung, welche Spannungsreferenz genutzt wird, erfolgt z.B. beim ATmega16 mit den Bits REFS1/REFS0 im ADMUX-Register. Die zu messende Spannung muss im Bereich zwischen &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AREF&#039;&#039;&#039; (egal ob intern oder extern) liegen. &lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; kann in zwei verschiedenen Betriebsarten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
; Einfache Wandlung (Single Conversion) : In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestoßen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
; Frei laufend (Free Running) : In dieser Betriebsart erfasst der Wandler permanent die anliegende Spannung und schreibt diese in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Die Register des ADC ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; verfügt über eigene Register. Im Folgenden die Registerbeschreibung eines  ATMega16, welcher über 8 ADC-Kanäle verfügt. Die Register unterscheiden sich jedoch nicht erheblich von denen anderer AVRs (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister A.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den &#039;&#039;&#039;ADC&#039;&#039;&#039; verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADEN&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able)&lt;br /&gt;
:Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren. Wenn das Bit nicht gesetzt ist, können die Pins wie normale I/O-Pins verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADSC&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion)&lt;br /&gt;
:Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.&lt;br /&gt;
:Wenn das Bit nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.&lt;br /&gt;
:Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADFR&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;F&#039;&#039;&#039;ree &#039;&#039;&#039;R&#039;&#039;&#039;un select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Ist das Bit auf 1 gesetzt arbeitet der ADC im Freerunning Modus. Dabe iwird das Datenregister permanent aktualisiert. Ist das Bit hingegen auf 0 gesetzt macht der ADC nur eine Single Conversion. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIF&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag)&lt;br /&gt;
:Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt, sobald eine Umwandlung erfolgt ist und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert wurde. Das Bit wird bei lesendem Zugriff auf &#039;&#039;&#039;ADC(L,H)&#039;&#039;&#039; automatisch (d.h. durch die Hardware) gelöscht.&lt;br /&gt;
:Wenn das &#039;&#039;&#039;ADIE&#039;&#039;&#039; Bit sowie das &#039;&#039;&#039;I-Bit&#039;&#039;&#039; im AVR &#039;&#039;&#039;Statusregister&#039;&#039;&#039; gesetzt ist, wird der &#039;&#039;&#039;ADC Interrupt&#039;&#039;&#039; ausgelöst und die Interrupt-Behandlungsroutine aufgerufen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn die Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 in das Register geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIE&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;, dann wird der &#039;&#039;&#039; ADC-Interrupt&#039;&#039;&#039; aktiviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADPS2...ADPS0&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;P&#039;&#039;&#039;rescaler &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
:Der &#039;&#039;&#039;ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz ergibt.&lt;br /&gt;
:Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
TF_{min}=\frac{CLK}{200\,\mathrm{kHz}}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50\,\mathrm{kHz}}=\frac{4000000}{50000}=\mathbf{80}&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im Interesse der schnelleren Wandlungszeit werden wir hier den Faktor 32 einstellen.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCL&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ADCH&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC &#039;&#039;&#039; Data Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn eine Umwandlung abgeschlossen ist, befindet sich der gemessene Wert in&lt;br /&gt;
diesen beiden Registern. Von &#039;&#039;&#039;ADCH&#039;&#039;&#039; werden nur die beiden niederwertigsten Bits verwendet. Es müssen immer beide Register ausgelesen werden, und zwar immer &#039;&#039;&#039;in der Reihenfolge: ADCL, ADCH&#039;&#039;&#039;. &lt;br /&gt;
Der effektive Messwert ergibt sich dann zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCL;       // mit uint16_t x&lt;br /&gt;
x += (ADCH&amp;lt;&amp;lt;8); // in zwei Zeilen (LSB/MSB-Reihenfolge und&lt;br /&gt;
                // C-Operatorpriorität sichergestellt)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADMUX&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Mu&#039;&#039;&#039;ltiple&#039;&#039;&#039;x&#039;&#039;&#039;er Select Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Register wird der zu messende Kanal ausgewählt. Beim 90S8535&lt;br /&gt;
kann jeder Pin von Port A als &#039;&#039;&#039;ADC&#039;&#039;&#039;-Eingang verwendet werden (=8 Kanäle).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADLAR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX4&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX3&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;REFS1...REFS0&#039;&#039;&#039; (&#039;&#039;&#039;Ref&#039;&#039;&#039;erence&#039;&#039;&#039;S&#039;&#039;&#039;election Bits)&lt;br /&gt;
:Mit diesen Bits kann die Referenzspannung eingestellt werden:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Referenzspanung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Externes AREF&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | AVCC als Referenz&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Interne 2,56 Volt&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADLAR&#039;&#039;&#039; (&#039;&#039;&#039;ADC &#039;&#039;&#039; &#039;&#039;&#039;L&#039;&#039;&#039;eft &#039;&#039;&#039;A&#039;&#039;&#039;djust &#039;&#039;&#039;R&#039;&#039;&#039;esult)&lt;br /&gt;
:Das ADLAR Bit verändert das Aussehen des Ergebnisses der AD-Wandlung. Bei einer logischen 1 wird das Ergebnis linksbündig ausgegeben, bei einer 0 rechtsündig. Eine Änderung in diesem Bit beeinflusst das Ergebnis sofort, ganz egal ob bereits eine Wandlung läuft.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX4...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 5 Bits wird der zu messende Kanal bestimmt. Wenn man einen einfachen 1-kanaligen ADC verwendet wird einfach die entsprechende Pinnummer des Ports in die Bits 0...2 eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
:Eine Empfehlung ist deswegen diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren des ADC ====&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039; ADC&#039;&#039;&#039; zu aktivieren, müssen wir das &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bit im &#039;&#039;&#039;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
Ein kleines Beispiel für den &amp;quot;single conversion&amp;quot;-Mode bei einem ATmega169 und Nutzung der internen Referenzspannung (beim &#039;169 1,1V bei anderen AVRs auch 2,56V). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler einstellen. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1023 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint16_t ReadChannel(uint8_t mux)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
&lt;br /&gt;
  ADMUX = mux;                      // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler &lt;br /&gt;
                               // setzen auf 8 (1) und ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  /* nach Aktivieren des ADC wird ein &amp;quot;Dummy-Readout&amp;quot; empfohlen, man liest&lt;br /&gt;
     also einen Wert und verwirft diesen, um den ADC &amp;quot;warmlaufen zu lassen&amp;quot; */&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
     ;     // auf Abschluss der Konvertierung warten &lt;br /&gt;
  }&lt;br /&gt;
  result = ADCW;  // ADCW muss einmal gelesen werden,&lt;br /&gt;
                  // sonst wird Ergebnis der nächsten Wandlung&lt;br /&gt;
                  // nicht übernommen.&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  result = 0; &lt;br /&gt;
  for( i=0; i&amp;lt;4; i++ )&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);            // eine Wandlung &amp;quot;single conversion&amp;quot;&lt;br /&gt;
    while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
      ;   // auf Abschluss der Konvertierung warten&lt;br /&gt;
    }&lt;br /&gt;
    result += ADCW;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Beispielaufrufe: */&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
&lt;br /&gt;
  adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -&amp;gt; Channel 0 */&lt;br /&gt;
  ...&lt;br /&gt;
  adcval = ReadChannel(2); /* MUX-Bits auf 0b0010 -&amp;gt; Channel 2 */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekennzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Das Löschen des ADIF-Flags sollte, &#039;&#039;&#039;entgegen&#039;&#039;&#039; der [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_intbits FAQ], mit&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADIF);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
erfolgen. Die Methode in der FAQ eignet sich nur für Register in denen &#039;&#039;&#039;nur&#039;&#039;&#039; Interrupt-Flags stehen.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Analog-Digital-Wandlung ohne internen ADC ===&lt;br /&gt;
&lt;br /&gt;
==== Messen eines Widerstandes ====&lt;br /&gt;
&lt;br /&gt;
Analoge Werte lassen sich ohne Analog-Digital-Wandler auch indirekt ermitteln. Im Folgenden wird die Messung des an einem Potentiometer eingestellten Widerstands anhand der Ladekurve eines Kondensators erläutert. Bei dieser Methode wird nur ein Portpin benötigt, ein Analog-Digital-Wandler oder Analog-Comparator ist nicht erforderlich. Es wird dazu ein Kondensator und der Widerstand (das Potentiometer) in Reihe zwischen Vorsorgungsspannung und Masse/GND geschaltet (sogen. RC-Netzwerk). Zusätzlich wird eine Verbindung der Leitung zwischen Kondensator und Potentiometer zu einem Portpin des Controllers hergestellt. Die folgende Abbildung verdeutlicht die erforderliche Schaltung. &lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wird der Portpin des Controllers auf Ausgang konfiguriert (im Beispiel &#039;&#039;DDRD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) und dieser Ausgang auf Logisch 1 (&amp;quot;High&amp;quot;, &#039;&#039;PORTD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) geschaltet, liegt an beiden &amp;quot;Platten&amp;quot; des Kondensators das gleiche Potential &#039;&#039;&#039;VCC&#039;&#039;&#039; an und der Kondensator somit entladen. (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so, da an beiden Seiten des Kondensators das gleiche Potential anliegt und somit eine Potentialdifferenz von 0V besteht =&amp;gt; Kondensator ist entladen).&lt;br /&gt;
&lt;br /&gt;
Nach einer gewissen Zeit ist der Kondensator entladen und der Portpin wird als Eingang konfiguriert (&#039;&#039;DDRD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2); PORTD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2)&#039;&#039;), wodurch dieser hochohmig wird. Der Status des Eingangspin (in PIND) ist Logisch 1 (High). Der Kondensator lädt sich jetzt über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), wird das Eingangssignal als LOW erkannt (Bit in PIND wird 0). Die Zeitspanne zwischen der Umschaltung von Entladung auf Aufladung und dem Wechsel des Eingangssignals von High auf Low ist ein Maß für den am Potentiometer eingestellten Widerstand. Zur Zeitmessung kann einer der im Controller vorhandenen Timer genutzt werden. Der 220 Ohm Widerstand dient dem Schutz des Controllers. Es würde sonst bei Maximaleinstellung des Potentionmeters (hier 0 Ohm) ein zu hoher Strom fließen, der die Ausgangsstufe des Controllers zerstört. &lt;br /&gt;
&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B: 0...100 % oder so) umzurechnen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Link 404 =&amp;gt; auskommentiert, mthomas 9.2.2008 &lt;br /&gt;
Ein Beispielprogramm findet sich auf [http://www.mypage.bluewin.ch/ch_schifferle/ Christian Schifferles Web-Seite] im Archiv &#039;&#039;ATMEL.ZIP&#039;&#039;, welches unter den Titel &#039;&#039;Tutorial &amp;quot;Programmieren mit C für Atmel Mikrocontroller&#039;&#039; heruntergeladen werden kann. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ADC über Komparator ====&lt;br /&gt;
&lt;br /&gt;
Es gibt einen weiteren Weg, eine analoge Spannung mit Hilfe des&lt;br /&gt;
Komparators, welcher in fast jedem AVR integriert ist, zu messen. Siehe dazu&lt;br /&gt;
auch die Application Note AVR400 von Atmel.&lt;br /&gt;
&lt;br /&gt;
Dabei wird das zu messende Signal auf den invertierenden Eingang&lt;br /&gt;
des Komparators geführt. Zusätzlich wird ein Referenzsignal an den nicht&lt;br /&gt;
invertierenden Eingang des Komparators angeschlossen. Das Referenzsignal wird&lt;br /&gt;
hier auch wieder über ein RC-Glied erzeugt, allerdings mit festen Werten für R&lt;br /&gt;
und C.&lt;br /&gt;
&lt;br /&gt;
[[Image:ADC ueber Komparator.gif]]&lt;br /&gt;
&lt;br /&gt;
Das Prinzip der Messung ist nun dem vorhergehenden recht&lt;br /&gt;
ähnlich. Durch Anlegen eines LOW-Pegels an Pin 2 wird der Kondensator zuerst&lt;br /&gt;
einmal entladen. Auch hier muss darauf geachtet werden, dass der Entladevorgang&lt;br /&gt;
genügend lang dauert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Nun wird Pin 2 auf HIGH gelegt. Der Kondensator wird geladen. Wenn die Spannung&lt;br /&gt;
über dem Kondensator die am Eingangspin anliegende Spannung erreicht hat&lt;br /&gt;
schaltet der Komparator durch. Die Zeit, welche benötigt wird, um den&lt;br /&gt;
Kondensator zu laden kann nun auch wieder als Maß für die Spannung an Pin 1&lt;br /&gt;
herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Ich habe es mir gespart, diese Schaltung auch aufzubauen und&lt;br /&gt;
zwar aus mehreren Gründen:&lt;br /&gt;
&lt;br /&gt;
# 3 Pins notwendig.&lt;br /&gt;
# Genauigkeit vergleichbar mit einfacherer Lösung.&lt;br /&gt;
# War einfach zu faul.&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung liegt allerdings darin, dass damit&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines Digital-Analog-Konverters (&#039;&#039;&#039;DAC&#039;&#039;&#039;) können wir nun auch Analogsignale ausgeben. Es gibt hier mehrere Verfahren. &amp;lt;!-- Wenn wir beim ADC die Möglichkeit haben, mit externen Komponenten zu operieren, müssen wir bei der DAC-Wandlung mit dem auskommen, was der Controller selber zu bieten hat. --mt: hmm, richtig? verstaendlich? redundant? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DAC über mehrere digitale Ausgänge ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir an den Ausgängen des Controllers ein entsprechendes&lt;br /&gt;
Widerstandsnetzwerk aufbauen haben wir die Möglichkeit, durch die Ansteuerung&lt;br /&gt;
der Ausgänge über den Widerständen einen Addierer aufzubauen, mit dessen&lt;br /&gt;
Hilfe wir eine dem Zahlenwert proportionale Spannung erzeugen können. Das&lt;br /&gt;
Schaltbild dazu kann etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
[[Image:DAC R2R.gif]]&lt;br /&gt;
&lt;br /&gt;
Es sollten selbstverständlich möglichst genaue Widerstände verwendet&lt;br /&gt;
werden, also nicht unbedingt solche mit einer Toleranz von 10% oder mehr.&lt;br /&gt;
Weiterhin empfiehlt es sich, je nach Anwendung den Ausgangsstrom über einen&lt;br /&gt;
Operationsverstärker zu verstärken.&lt;br /&gt;
&lt;br /&gt;
=== PWM (Pulsweitenmodulation) ===&lt;br /&gt;
&lt;br /&gt;
Wir kommen nun zu einem Thema, welches in aller Munde ist, aber viele&lt;br /&gt;
Anwender verstehen nicht ganz, wie &#039;&#039;&#039; PWM&#039;&#039;&#039; eigentlich funktioniert.&lt;br /&gt;
&lt;br /&gt;
Wie wir alle wissen, ist ein Mikrocontroller ein rein digitales Bauteil.&lt;br /&gt;
Definieren wir einen Pin als Ausgang, dann können wir diesen Ausgang entweder&lt;br /&gt;
auf HIGH setzen, worauf am Ausgang die Versorgungsspannung &#039;&#039;&#039; Vcc&#039;&#039;&#039; anliegt, oder aber wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt. Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen HIGH und LOW umschalten? - Richtig, wir erhalten eine Rechteckspannung, wie die folgende Abbildung zeigt:&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 1.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Rechteckspannung hat nun einen arithmetischen Mittelwert, &lt;br /&gt;
der je nach Pulsbreite kleiner oder größer ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 2.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun diese pulsierende Ausgangsspannung noch über ein RC-Glied filtern/&amp;quot;glätten&amp;quot;, dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVRs können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. &lt;br /&gt;
Dazu dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt, weshalb ein Blick ins entsprechende Datenblatt dringend angeraten wird.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren, müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| PWM-Modus des Timers ist nicht aktiv.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Timer/Counter zählt nun permanent von 0 bis zur Obergrenze&lt;br /&gt;
und wieder zurück, er wird also als sogenannter Auf-/Ab Zähler betrieben. &lt;br /&gt;
Die Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss mit den Bits &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; desselben&lt;br /&gt;
Registers die gewünschte Ausgabeart des Signals definiert werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Nicht invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Hochzählen und gesetzt beim&lt;br /&gt;
Herunterzählen.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Herunterzählen und gesetzt beim&lt;br /&gt;
Hochzählen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl, um beispielsweise den Timer/Counter als&lt;br /&gt;
nicht invertierenden 10-Bit PWM zu verwenden, heißt dann:&lt;br /&gt;
&lt;br /&gt;
alte Schreibweise (PWMxx wird nicht mehr akzeptiert)&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;PWM11)|(1&amp;lt;&amp;lt;PWM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
neue Schreibweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;WGM11)|(1&amp;lt;&amp;lt;WGM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Timer/Counter überhaupt läuft, müssen wir im Control&lt;br /&gt;
Register B &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; noch den gewünschten Takt (Vorteiler) einstellen und&lt;br /&gt;
somit auch die Frequenz des &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signals bestimmen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stop. Der Timer/Counter wird gestoppt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin 1, negative Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin 1, positive Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Also um einen Takt von CK / 1024 zu generieren, verwenden wir&lt;br /&gt;
folgenden Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1B = (1&amp;lt;&amp;lt;CS12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt muss nur noch der Vergleichswert festgelegt werden. Diesen&lt;br /&gt;
schreiben wir in das 16-Bit Timer/Counter Output Compare Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem Vergleichswert und dem generierten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal aufzeigen.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
Ach ja, fast hätte ich&#039;s vergessen. Das generierte &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal&lt;br /&gt;
wird am Output Compare Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; des Timers ausgegeben und leider können wir&lt;br /&gt;
deshalb auch beim AT90S2313 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren. Andere AVR-Typen verfügen über bis zu vier PWM-Ausgänge. Zu beachten ist außerdem, das wenn der OC Pin aktiviert ist, er nichtmehr wie üblich funktioniert und z.B. nicht einfach über PINx ausgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Ein Programm, dass an einem ATMega8 den Fast-PWM Modus verwendet, den Modus 14, könnte so aussehen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // OC1A auf Ausgang&lt;br /&gt;
  DDRB = (1 &amp;lt;&amp;lt; PB1 );&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // Timer 1 einstellen&lt;br /&gt;
  //  &lt;br /&gt;
  // Modus 14:&lt;br /&gt;
  //    Fast PWM, Top von ICR1&lt;br /&gt;
  //&lt;br /&gt;
  //     WGM13    WGM12   WGM11    WGM10&lt;br /&gt;
  //      1        1       1        0&lt;br /&gt;
  //&lt;br /&gt;
  //    Timer Vorteiler: 1&lt;br /&gt;
  //     CS12     CS11    CS10&lt;br /&gt;
  //       0        0       1&lt;br /&gt;
  //&lt;br /&gt;
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match&lt;br /&gt;
  //     COM1A1   COM1A0&lt;br /&gt;
  //       1        0&lt;br /&gt;
 &lt;br /&gt;
  TCCR1A = (1&amp;lt;&amp;lt;COM1A1) | (1&amp;lt;&amp;lt;WGM11);&lt;br /&gt;
  TCCR1B = (1&amp;lt;&amp;lt;WGM13) | (1&amp;lt;&amp;lt;WGM12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  //  den Endwert (TOP) für den Zähler setzen&lt;br /&gt;
  //  der Zähler zählt bis zu diesem Wert&lt;br /&gt;
&lt;br /&gt;
  ICR1 = 0x6FFF;&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  // der Compare Wert&lt;br /&gt;
  // Wenn der Zähler diesen Wert erreicht, wird mit&lt;br /&gt;
  // obiger Konfiguration der OC1A Ausgang abgeschaltet&lt;br /&gt;
  // Sobald der Zähler wieder bei 0 startet, wird der&lt;br /&gt;
  // Ausgang wieder auf 1 gesetzt&lt;br /&gt;
  //&lt;br /&gt;
  // Durch Verändern dieses Wertes, werden die unterschiedlichen&lt;br /&gt;
  // PWM Werte eingestellt.&lt;br /&gt;
&lt;br /&gt;
  OCR1A = 0x3FFF;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  while( 1 )&lt;br /&gt;
    ;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für Details der PWM Möglichkeiten, muß immer das jeweilge Datenblatt des Prozessors konsultiert werden, da sich die unterschiedlichen Prozessoren in ihren Möglichkeiten doch stark unterscheiden. Auch muß man aufpassen, welches zu setzende Bit in welchem Register sind. Auch hier kann es sein, dass gleichnamige Konfigurationsbits in unterschiedlichen Konfigurationsregistern (je nach konkretem Prozessortyp) sitzen.&lt;br /&gt;
&lt;br /&gt;
= LCD-Ansteuerung =&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung an der LCD-Controller-Platine ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; (ist in manchen Unterlagen auch &#039;&#039;&#039;EN&#039;&#039;&#039;  für &#039;&#039;Enable&#039;&#039; abgekürzt) benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert, ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitetet hat (diese Methode u.a. in der LCD-Library von Peter Fleury verwendet). Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt da wir wissen, welche Anschlüsse das LCDs benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin #-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin-µC&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND oder Poti (siehe oben)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD4 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD5 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD0 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD1 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD2 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD3 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man die Steuerleitungen EN und RS auf Pins an einem anderen Port legen möchte, kann man so wie in diesem [http://www.mikrocontroller.net/topic/88543#751982 Forumsbeitrag] vorgehen.&lt;br /&gt;
&lt;br /&gt;
Ok, alles ist verbunden, wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display? &lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
void lcd_data(unsigned char temp1);&lt;br /&gt;
void lcd_string(char *data);&lt;br /&gt;
void lcd_command(unsigned char temp1);&lt;br /&gt;
void lcd_enable(void);&lt;br /&gt;
void lcd_init(void);&lt;br /&gt;
void lcd_home(void);&lt;br /&gt;
void lcd_clear(void);&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y);&lt;br /&gt;
&lt;br /&gt;
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
&lt;br /&gt;
// LCD Befehle&lt;br /&gt;
&lt;br /&gt;
#define CLEAR_DISPLAY 0x01&lt;br /&gt;
#define CURSOR_HOME   0x02&lt;br /&gt;
&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
&lt;br /&gt;
#define LCD_PORT      PORTD&lt;br /&gt;
#define LCD_DDR       DDRD&lt;br /&gt;
#define LCD_RS        PD4&lt;br /&gt;
#define LCD_EN        PD5&lt;br /&gt;
// DB4 bis DB7 des LCD sind mit PD0 bis PD3 des AVR verbunden&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// sendet ein Datenbyte an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_data(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_RS);        // RS auf 1 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_command(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);        // RS auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;              // oberes Nibble holen&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;            // maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;            // unteres Nibble holen und maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// erzeugt den Enable-Puls&lt;br /&gt;
void lcd_enable(void)&lt;br /&gt;
{&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
    _delay_us(1);                   // kurze Pause&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/80900&lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Initialisierung: &lt;br /&gt;
// Muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
void lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
   LCD_DDR = LCD_DDR | 0x0F | (1&amp;lt;&amp;lt;LCD_RS) | (1&amp;lt;&amp;lt;LCD_EN);   // Port auf Ausgang schalten&lt;br /&gt;
&lt;br /&gt;
   // muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(15);&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x03;            &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);      // RS auf 0&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4 Bit Modus aktivieren &lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x02;&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4Bit / 2 Zeilen / 5x7&lt;br /&gt;
   lcd_command(0x28);&lt;br /&gt;
    &lt;br /&gt;
   // Display ein / Cursor aus / kein Blinken&lt;br /&gt;
   lcd_command(0x0C); &lt;br /&gt;
 &lt;br /&gt;
   // inkrement / kein Scrollen&lt;br /&gt;
   lcd_command(0x06);&lt;br /&gt;
&lt;br /&gt;
   lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
&lt;br /&gt;
void lcd_clear(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CLEAR_DISPLAY);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
&lt;br /&gt;
void lcd_home(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CURSOR_HOME);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)&lt;br /&gt;
&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t tmp;&lt;br /&gt;
&lt;br /&gt;
  switch (y) {&lt;br /&gt;
    case 1: tmp=0x80+0x00+x; break;    // 1. Zeile&lt;br /&gt;
    case 2: tmp=0x80+0x40+x; break;    // 2. Zeile&lt;br /&gt;
    case 3: tmp=0x80+0x10+x; break;    // 3. Zeile&lt;br /&gt;
    case 4: tmp=0x80+0x50+x; break;    // 4. Zeile&lt;br /&gt;
  }&lt;br /&gt;
  lcd_command(tmp);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_string(char *data)&lt;br /&gt;
{&lt;br /&gt;
    while(*data) {&lt;br /&gt;
        lcd_data(*data);&lt;br /&gt;
        data++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches die Funktionen benutzt, sieht zb. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen&lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    lcd_data(&#039;T&#039;);&lt;br /&gt;
    lcd_data(&#039;e&#039;);&lt;br /&gt;
    lcd_data(&#039;s&#039;);&lt;br /&gt;
    lcd_data(&#039;t&#039;);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
&lt;br /&gt;
    lcd_string(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist dabei, dass die Optimierung bei der Compilierung eingeschaltet ist, sonst stimmen die Zeiten der Funktionen _delay_us() und _delay_ms() nicht und der Code wird wesentlich länger (Siehe Dokumentation der libc im WinAVR).&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches eine Variable ausgibt, sieht zb. so aus.&lt;br /&gt;
Mittels der itoa() Funktion (itoa = &amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;nteger &amp;lt;b&amp;gt;To&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;A&amp;lt;/b&amp;gt;scii ) wird von einem Zahlenwert eine textuelle Repräsentierung ermittelt (sprich: ein String erzeugt) und dieser String mit der bereits vorhandenen Funktion lcd_string ausgegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen &lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Beispiel&lt;br /&gt;
int variable = 42;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe des Zeichens dessen ASCII-Code gleich dem Variablenwert ist&lt;br /&gt;
    // (Im Beispiel entspricht der ASCII-Code 42 dem Zeichen *)&lt;br /&gt;
    // http://www.code-knacker.de/ascii.htm&lt;br /&gt;
    lcd_data(variable);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
 &lt;br /&gt;
    // Ausgabe der Variable als Text in dezimaler Schreibweise&lt;br /&gt;
    {&lt;br /&gt;
       // ... umwandeln siehe FAQ Artikel bei www.mikrocontroller.net&lt;br /&gt;
       // WinAVR hat eine itoa()-Funktion, das erfordert obiges #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
       char Buffer[20]; // in diesem {} lokal&lt;br /&gt;
       itoa( variable, Buffer, 10 ); &lt;br /&gt;
&lt;br /&gt;
       // ... ausgeben  &lt;br /&gt;
       lcd_string( Buffer );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einrichten eines Projekts muss man zu der Datei mit dem Hauptprogramm auch die Datei lcd-routines.c in das Projekt aufnehmen. Dies geschieht beim AVR Studio unter Source Files im Fenster AVR GCC oder bei WinAVR im Makefile (z.B. durch SRC += lcd-routines.c).&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVRs sind für viele Steuerungsaufgaben zu schnell. Wenn wir beispielsweise eine LED oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die CPU-Frequenz verwenden, da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, Vorgänge in Zeitabständen durchzuführen, die geringer als die Taktfrequenz des Controllers sind. Selbstverständlich sollte die resultierende Frequenz auch noch möglichst genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist ganz einfach ein bestimmtes Register im µC, das völlig ohne Zutun des Programms, also per Hardware, hochgezählt wird. Das alleine wäre noch nicht allzu nützlich, wenn nicht dieses Hardwareregister bei bestimmten Zählerständen einen Interrupt auslösen könnte. Ein solches Ereignis ist der Overflow: Da die Bitbreite des Registers beschränkt ist, kommt es natürlich auch vor, dass der Zähler so hoch zählt, dass der nächste Zählerstand mit dieser Bitbreite nicht mehr darstellbar ist und der Zähler wieder auf 0 zurückgesetzt wird. Dieses Ereignis nennt man den Overflow und es ist möglich an dieses Ereignis einen Interrupt zu koppeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Alternativvorschlag mthomas &lt;br /&gt;
Jeder Timer verfügt über ein Zählerregister im Mikrocontroller, das automatisch und ohne Zutun des Programms von der Hardware weitergezählt wird. In einem einfachen Anwendungsfall stellt man den Timer auf eine Zählgeschwindigkeit (Frequenz) und kann dann anhand des Zählerstands ermitteln, wie viel Zeit vergangen ist. Das eigentlich Nützliche an Timern ist jedoch, dass man  bestimmte Zählerstände mit Interrupts verknüpfen kann, so dass der Controller beim Auftreten automatisch eine vom Anwender geschriebene Routine aufruft. Eines dieser möglichen Ereignis ist der Overflow ((Zähler-)Überlauf), der dann auftritt, wenn der Wert des Zählerregisters (Timer/Counter-Register) den maximal möglichen Wert überschreitet. Der Maximalwert wird durch die Bitbreite des Zählerregisters bestimmt (z.B. 255 bei 8-Bit Timern). Beim Überlauf/Overflow wird der Zähler durch die Hardware auf 0 zurückgesetzt und die Zählung beginnt von neuem. Wurde vorher der Overflow-Interrupt für den Timer aktiviert (im Timer Control Register) unterbricht der Controller automatisch die Ausführung des Hauptprogramms und verzweigt in die Interrupt-Routine des Anwenders.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auf den AT90S2313. Für andere Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer Auflösung von 65536. Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz, der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
== Der Vorteiler (Prescaler) ==&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler dient dazu, den CPU-Takt vorerst um einen einstellbaren Faktor zu reduzieren. Die so geteilte Frequenz wird den Eingängen der Timer zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024 einstellen, wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca. 4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw. Zählregister (TCNTx) mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle verfügen über mindestens einen, teilweise sogar zwei, 8-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird z.B bei AT90S2313 über folgende Register angesprochen (bei anderen Typen weitestgehend analog):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    TCCR0 |= (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst und die daran gebundene Interrupt-Routine abgearbeitet. Das TOV Flag &lt;br /&gt;
lässt sich durch das Hineinschreiben einer 1 und nicht wie erwartet einer 0 wieder zurücksetzen.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen außer den 8-Bit Timern auch 16-Bit Timer. Die 16-Bit Timer/Counter sind etwas komplexer aufgebaut als die 8-Bit Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM13&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM12 (CTC1)&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorteiler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt, und C der voreingestellte Vergleichswert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin T1, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin T1, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T1 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T1 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0, bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;TCNT1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;OCR1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;OCR1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;OCR1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;OCR1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäß folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]] [[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Ausserdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr. Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen kleiner 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4us warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.6 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t x;&lt;br /&gt;
&lt;br /&gt;
    x = 10;&lt;br /&gt;
&lt;br /&gt;
    while (x &amp;gt;= 0)&lt;br /&gt;
    {&lt;br /&gt;
      // tu was&lt;br /&gt;
&lt;br /&gt;
      x--;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog? ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Einige Controller haben einen eigenen Watchdog Oszillator, z.B. der Tiny2313 mit 128kHz. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde, beginnt der Counter von 0 an hochzuzählen. &lt;br /&gt;
Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern, müssen wir den Watchdog regelmäßig wieder neu starten bzw. rücksetzen (Watchdog Reset). &lt;br /&gt;
Dies sollte innerhalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern, muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten. Es müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. &lt;br /&gt;
Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist, wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei &#039;&#039;wdt.h&#039;&#039; (&#039;&#039;#include &amp;lt;avr/wdt.h&amp;gt;&#039;&#039;) in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch gestartet wird. &lt;br /&gt;
Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. &lt;br /&gt;
Falls ein anderer Wert gewünscht ist, so kann dies im Makfile in den Linker-Optionen eingetragen werden. &lt;br /&gt;
Dazu muss in der Zeile LDFLAGS folgende Option angefügt werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert&lt;br /&gt;
:Mögliche Timeoutwerte:&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Konstante&lt;br /&gt;
! Wert&lt;br /&gt;
! TimeOut&lt;br /&gt;
|- &lt;br /&gt;
| WDTO_15MS   &lt;br /&gt;
| 0&lt;br /&gt;
| 15 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_30MS   &lt;br /&gt;
| 1&lt;br /&gt;
| 30 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_60MS   &lt;br /&gt;
| 2&lt;br /&gt;
| 60 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_120MS   &lt;br /&gt;
| 3&lt;br /&gt;
| 120 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_250MS   &lt;br /&gt;
| 4&lt;br /&gt;
| 250 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_500MS   &lt;br /&gt;
| 5&lt;br /&gt;
| 500 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_1S   &lt;br /&gt;
| 6&lt;br /&gt;
| 1 S&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_2S   &lt;br /&gt;
| 7&lt;br /&gt;
| 2s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
== Watchdog-Anwendungshinweise ==&lt;br /&gt;
&lt;br /&gt;
Ob nun der Watchdog als Schutzfunktion überhaupt verwendet werden soll, hängt stark von der Anwendung, der genutzten Peripherie und dem Umfang und der Qualitätssicherung des Codes ab. Will man sicher gehen, dass ein Programm sich nicht in einer Endlosschleife verfängt, ist der Wachdog das geeignete Mittel dies zu verhindern. Weiterhin kann bei geschickter Programmierung der Watchdog dazu genutzt werden, bestimmte Stromsparfunktionen zu implementieren. Bei einigen neueren AVRs (z.B. dem ATTiny13) kann der Watchdog auch direkt als Timer genutzt werden, der den Controller aus einem Schlafmodus aufweckt. Auch dies kann im &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register eingestellt werden. Außerdem bietet der WD die einzige Möglichkeit einen beabsichtigten System-Reset (ein &amp;quot;richtiger Reset&amp;quot;, kein &amp;quot;jmp 0x0000&amp;quot;) ohne externe Beschaltung auszulösen, was z.B. bei der Implementierung eines Bootloaders nützlich ist. Bei bestimmten Anwendungen kann die Nutzung des WD als &amp;quot;ultimative Deadlock-Sicherung für nicht bedachte Zustände&amp;quot; natürlich immer als zusätzliche Sicherung dienen. &lt;br /&gt;
&lt;br /&gt;
Es besteht die Möglichkeit herauszufinden, ob ein Reset durch den Watchdog ausgelöst wurde (beim ATmega16 z.B. Bit WDRF in MCUCSR). Diese Information sollte auch genutzt werden, falls ein WD-Reset in der Anwendung nicht planmäßig implementiert wurde. Zum Beispiel kann man eine LED an einen freien Pin hängen, die nur bei einem Reset durch den WD aufleuchtet oder aber das &amp;quot;Ereignis WD-Reset&amp;quot; im internen EEPROM des AVR absichern, um die Information später z.B. über UART oder ein Display auszugeben (oder einfach den EEPROM-Inhalt über die ISP/JTAG-Schnittstelle auslesen).&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/77273#642501 Bug in ATtiny2313?]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt der Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft. Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe Abschnitt (TODO: verlinken) im Anhang.&lt;br /&gt;
&amp;lt;!--Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. vgl. [[AVR-GCC-Tutorial#Anhang|Anhang]])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.B. &#039;&#039;void TIMER0_OVF_vect(void)...&#039;&#039;). Der Unterschied im Vergleich zu ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben.  &amp;lt;!--Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)), als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur &lt;br /&gt;
in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechnungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechnungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0, PORTA ist danach 0b00000001. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-oder-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8 (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten &#039;&#039;Sleep-Modes&#039;&#039; (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_enable()&#039;&#039;&#039;&lt;br /&gt;
:aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_cpu()&#039;&#039;&#039;&lt;br /&gt;
: Versetzt den Controller in den Schlafmodus (sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt)&lt;br /&gt;
* &#039;&#039;&#039;sleep_disable()&#039;&#039;&#039;&lt;br /&gt;
:deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts.&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;allozieren&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich, um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory, bei AVR Controllern mit mehr als 64kB Flash auch elpm). Die Standard-Laufzeitbibliothek des avr-gcc (die avr-libc) stellt diese Funktionen nach Einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Lokale Variablen (eigentlich Konstanten) innerhalb von Funktionen können ebenfalls im Programmspeicher abgelegt werden. Dazu ist bei der Definition jedoch ein &#039;&#039;static&#039;&#039; voranzustellen, da solche &amp;quot;Variablen&amp;quot; nicht auf dem Stack bzw. (bei Optimierung) in Registern verwaltet werden können. Der Compiler &amp;quot;wirft&amp;quot; eine Warnung falls static fehlt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray2 };&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  static /*const*/ uint8_t pgmTestByteLocal PROGMEM = 0x55;&lt;br /&gt;
  static /*const*/ char pgmTestStringLocal[] PROGMEM = &amp;quot;im Flash&amp;quot;;&lt;br /&gt;
  // so nicht (static fehlt): char pgmTestStringLocalFalsch [] PROGMEM = &amp;quot;so nicht&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 // ...&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
    //...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte....&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWort);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;groß&amp;quot; (Stand avr-gcc 3.4.x). Damit ist der mögliche Speicherbereich für &amp;quot;Flash-Konstanten&amp;quot; auf 64kB begrenzt.  &amp;lt;!-- Einige avr-libc/pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher) (intern via Assembler Anweisung ELPM). Die Initialisierungswerde des Speicherinhalts jenseits der 64kB-Marke müssen dann jedoch auf anderem Weg angelegt werden (nicht PROGMEM, evtl. eigene Section und Linker-Optionen - TODO) /// alt - und nicht ganz korrekt: (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.)--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray enthält nun die Startadresse des Byte-Arrays pgmFooByteArray1&lt;br /&gt;
    // Allerdings würde ein direkter Zugriff mit diesem Pointer (z.B. temp=*ptrToArray)&lt;br /&gt;
    // &#039;&#039;&#039;nicht&#039;&#039;&#039; den Inhalt von pgmFooByteArray1[0] liefern, sondern von einer Speicherstelle&lt;br /&gt;
    // im &#039;&#039;&#039;RAM&#039;&#039;&#039;, die die gleiche Adresse hat wie pgmFooByteArray1[0]&lt;br /&gt;
    // Daher muss nun die Funktion pgm_read_byte() benutzt werden, die die in ptrToArray&lt;br /&gt;
    // enthaltene Adresse benutzt und auf das Flash zugreift.&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray enthält nun die Adresse des ersten Elements des Byte-Arrays pgmFooByteArray2&lt;br /&gt;
    // da im zweiten Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmFooByteArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Array aus Zeichenketten im Flash-Speicher ===&lt;br /&gt;
&lt;br /&gt;
Felder aus Zeichenketten im Flash-Speicher werden in zwei Schritten angelegt: Zuerst die einzelnen Elemente des Arrays und im Anschluss ein Array, in dem die Addressen der Zeichenketten abgelegt werden. Zum Auslesen wird zuerst die Adresse des i-ten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, auf das Element (die Zeichenkette) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const char str1[] PROGMEM = &amp;quot;first_A&amp;quot;;&lt;br /&gt;
const char str2[] PROGMEM = &amp;quot;second_A&amp;quot;;&lt;br /&gt;
const char str3[] PROGMEM = &amp;quot;third_A&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char *strarray1[] PROGMEM = {&lt;br /&gt;
	str1,&lt;br /&gt;
	str2,&lt;br /&gt;
	str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	int i, j, l;&lt;br /&gt;
	const char *pstrflash;&lt;br /&gt;
	char work[20], work2[20];&lt;br /&gt;
	// fuer Simulation: per volatile Optimierung verhindern, &lt;br /&gt;
	//                  da c nicht genutzt&lt;br /&gt;
	volatile char c;&lt;br /&gt;
	&lt;br /&gt;
	for ( i = 0; i &amp;lt; (sizeof(strarray1)/sizeof(strarray1[0]) ); i++ ) {&lt;br /&gt;
&lt;br /&gt;
		// setze Pointer auf die Addresse des i-ten Elements des&lt;br /&gt;
		// &amp;quot;Flash-Arrays&amp;quot; (str1, str2, ...)&lt;br /&gt;
		pstrflash = (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) );&lt;br /&gt;
		&lt;br /&gt;
		// kopiere den Inhalt der Zeichenkette von der&lt;br /&gt;
		// in pstrflash abgelegten Adresse in das work-Array&lt;br /&gt;
		// analog zu strcpy( work, strarray1[i]) wenn alles im RAM&lt;br /&gt;
		strcpy_P( work, pstrflash );&lt;br /&gt;
		// verkuerzt:&lt;br /&gt;
		strcpy_P( work2, (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) ) );&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
		// Zeichen-fuer-Zeichen&lt;br /&gt;
		l = strlen_P( pstrflash );&lt;br /&gt;
		for ( j=0; j &amp;lt; l; j++ ) {&lt;br /&gt;
			// analog zu c=strarray[i][j] wenn alles im RAM&lt;br /&gt;
			c = (char)( pgm_read_byte( pstrflash++ ) );&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	while (1) { ; }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: &amp;quot;How do I put an array of strings completely in ROM?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5)) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Vorsicht: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char textImFlashOK[] PROGMEM = &amp;quot;mit[]&amp;quot;; &lt;br /&gt;
// = Daten im &amp;quot;Flash&amp;quot;, textImFlashOK* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char* textImFlashProblem PROGMEM = &amp;quot;mit*&amp;quot;;&lt;br /&gt;
// Konflikt: Daten im BSS (lies: RAM), textImFlashFAIL* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der Initialisierungsstring von &amp;quot;textImFlashProblem&amp;quot; zu den Konstanten ans Ende des Programmcodes gelegt wird (BSS), von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte (und wird). Da der lesende Code (mittels pgm_read*) trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein weiters Problem des AVR-GCC (gesehen bei avr-gcc 3.4.1 und 3.4.2) bei der Anpassung an die Harvard-Architektur zu sein (konstanter Pointer auf variable Daten?!). Abhilfe (&amp;quot;Workaround&amp;quot;): Initialisierung bei Zeichenketten mit [] oder gleich im Code PSTR(&amp;quot;...&amp;quot;) nutzen.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden, ob es sich um eine Adresse im Flash oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind.&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die einen im Flash abgelegten String z.B. an eine UART ausgibt, würde dann so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void uart_puts_p(const char *text)&lt;br /&gt;
{&lt;br /&gt;
    char Zeichen;&lt;br /&gt;
&lt;br /&gt;
    while (Zeichen = pgm_read_byte(text))&lt;br /&gt;
    {   /* so lange, wie mittels pgm_read_byte ein Zeichen vom Flash gelesen&lt;br /&gt;
           werden konnte, welches nicht das &amp;quot;String-Endezeichen&amp;quot; darstellt */&lt;br /&gt;
&lt;br /&gt;
        /* Das gelesene Zeichen über die normalen Kanäle verschicken */&lt;br /&gt;
        uart_putc(Zeichen);&lt;br /&gt;
        text++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Flash in der Anwendung schreiben ===&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option (auch bekannt als Bootloader-Support) können Teile des Flash-Speichers auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. Bei wenigen &amp;quot;kleinen&amp;quot; AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm(Flash)- und Datenspeicher(RAM) auf. Der C-Standard und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiß&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, dass vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgegeben sind, die innerhalb weniger Taktzyklen aufeinanderfolgen müssen (&amp;quot;timed sequence&amp;quot;). Auch dies muss bei Nutzung der Funktionen aus der avr-libc/eeprom.h-Datei nicht selbst implementiert werden. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Siehe dazu folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt; // wird in aktuellen Versionen der avr-lib mit xx.h eingebunden&lt;br /&gt;
&lt;br /&gt;
// EEMEM wird bei aktuellen Versionen der avr-lib in eeprom.h definiert&lt;br /&gt;
// hier: definiere falls noch nicht bekannt (&amp;quot;alte&amp;quot; avr-libc)&lt;br /&gt;
#ifndef EEMEM&lt;br /&gt;
// alle Textstellen EEMEM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEMEM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
//...&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // &#039;Variablen&#039; eeFooByte geschrieben&lt;br /&gt;
//...&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z.B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    #define EEPROM_DEF 0xFF&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ( ( myByte = eeprom_read_byte(&amp;amp;eeFooByte) ) == EEPROM_DEF ) {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
//...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprüft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und möglicherweise noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,3);&lt;br /&gt;
&lt;br /&gt;
    /* dito etwas anschaulicher aber &amp;quot;unnütze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fließkommazahlen lassen sich recht praktisch über eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t i[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    uint8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEMEM;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
//...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklaration abgeleiteten EEPROM-Inhalt in eine Datei geschrieben werden (übliche Dateiendung: .eep, Daten im Intel Hex-Format). Damit können recht elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen (siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles). Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden (Write EEPROM), wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse (vgl. Datenblatt Abschnitt Fuse Bits) die vorherigen Daten (EESAVE programmed = 0), deren Position möglicherweise nicht mehr mit der Belegung im aktuellen Programm übereinstimmt oder den Standardwert nach &amp;quot;Chip Erase&amp;quot;: 0xFF (EESAVE unprogrammed = 1). Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Eine besondere Funktion des avr-gcc ist, dass mit entsprechenden Optionen im Makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Variable auf feste Adressen legen ===&lt;br /&gt;
&lt;br /&gt;
Gleich zu Beginn möchte ich darauf hinweisen, dass dieses Verfahren nur ein Workaround ist, mit dem man das Problem der anscheinend &amp;quot;zufälligen&amp;quot; Verteilung&lt;br /&gt;
der EEPROM-Variablen durch den Compiler etwas in den Griff bekommen kann.&lt;br /&gt;
&lt;br /&gt;
Hilfreich kann dies vor allem dann sein, wenn man z.B. über einen Kommandointerpreter (o.ä. Funktionen) direkt bestimmte EEPROM-Adressen manipulieren möchte. Auch wenn man über einen JTAG-Adapter (mk I oder mkII) den Programmablauf manipulieren möchte, indem man die EEPROM-Werte direkt ändert, kann diese Technik hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Im folgenden nun zwei Sourcelistings mit einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.h&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#inlcude &amp;lt;avr/eeprom.h&amp;gt;     // Die EEPROM-Definitionen/Macros der avr-libc einbinden&lt;br /&gt;
&lt;br /&gt;
#define   EESIZE   512      // Maximale Größe des EEPROMS&lt;br /&gt;
&lt;br /&gt;
#define   EE_DUMMY   0x000  // Dummyelement (Adresse 0 sollte nicht genutzt werden)&lt;br /&gt;
#define   EE_VALUE1  0x001  // Eine Bytevariable  &lt;br /&gt;
#define   EE_WORD1L  0x002  // Eine Wordvariable (Lowbyte)&lt;br /&gt;
#define   EE_WORD1H  0x003  // Eine Wordvariable (Highbyte)&lt;br /&gt;
#define   EE_VALUE2  0x004  // Eine weitere Bytevariable&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den Macros &#039;&#039;&#039;#define EE_VALUE1&#039;&#039;&#039; legt man den Namen und die Adresse der&lt;br /&gt;
&#039;Variablen&#039; fest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Die Adressen sollten fortlaufend, zumindest aber aufsteigend sortiert sein! Ansonsten besteht die Gefahr, daß man sehr schnell ein Durcheinander im EEPROM Speicher veranstaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Für den Compiler sind das lediglich Speicher-Adressen, über die auf das EEPROM zugegriffen wird. Der Compiler sieht nichts davon als eine echte Variable an und stößt sich daher auch nicht daran, wenn 2 Makros mit der gleichen Speicheradresse, bzw. überlappenden Speicherbereichen definiert werden. Es liegt einzig und alleine in der Hand des Programmierers, hier keinen Fehler zu machen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
   [EE_DUMMY]   = 0x00,&lt;br /&gt;
   [EE_VALUE1]  = 0x05,&lt;br /&gt;
   [EE_WORD1L]  = 0x01,   &lt;br /&gt;
   [EE_WORD1H]  = 0x00,&lt;br /&gt;
   [EE_VALUE2]  = 0xFF&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch die Verwendung eines Array, welches das gesammte EEPROM umfasst, bleibt&lt;br /&gt;
dem Compiler nicht anderes übrig, als das Array so zu platzieren, dass Element 0&lt;br /&gt;
des Arrays der Adresse 0 des EEPROMs entspricht. (&#039;&#039;Ich hoffe nur, dass die Compilerbauer daran nichts ändern!&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Wie man in dem obigen Codelisting auch sehen kann, hat das Verfahren einen kleinen Haken. Variablen die größer sind als 1 Byte, müssen etwas umständlicher&lt;br /&gt;
definiert werden. Benötigt man keine Initialisierung durch das Programm (was der Normalfall sein dürfte), dann kann man das auch so machen:&lt;br /&gt;
&lt;br /&gt;
Möchte man im EEPROM hintereinander beispielsweise Variablen, mit den Namen &#039;&#039;&#039;Wert&#039;&#039;&#039;, &#039;&#039;&#039;Anzahl&#039;&#039;&#039;, &#039;&#039;&#039;Name&#039;&#039;&#039; und &#039;&#039;&#039;Wertigkeit&#039;&#039;&#039; definieren, wobei Wert und Wertigkeit 1 Byte belegen sollen, Anzahl als 1 Wort (also 2 Bytes) und Name mit 10 Bytes reserviert werden soll, so geht auch folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes Makro definiert also seine Startadresse durch die Startadresse der unmittelbar vorhergehende &#039;Variablen&#039; plus der Anzahl der Bytes die von der vorhergehenden &#039;Variablen&#039; verbraucht werden. Dadurch ist man zumindest etwas auf der sicheren Seite, dass keine 2 &#039;Variablen&#039; im EEPROM überlappend definiert werden. Möchte man eine weitere &#039;Variable&#039; hinzufügen, so wird deren&lt;br /&gt;
Name, einfach anstelle der EE_LAST eingesetzt und eine neue Zeile für EE_LAST eingefügt, in der dann die Größe der &#039;Variablen&#039; festgelegt wird. Zb.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_PROZENT    ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( double ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EE_PROZENT legt die Startadresse für eine neue &#039;Variable&#039; des Datentyps double fest.&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf die EEPROM Werte kann dann z.B.so erfolgen:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t   temp1;&lt;br /&gt;
uint16_t  temp2;&lt;br /&gt;
&lt;br /&gt;
temp1 = eeprom_read_byte(EE_VALUE1);&lt;br /&gt;
temp2 = eeprom_read_word(EE_WORD1L);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ob die in der avr-libc vorhandenen Funktionen dafür verwendet werden können, weiß ich nicht. Aber in einigen Fällen muss man sich sowieso eigene Funktionen&lt;br /&gt;
bauen, welche die spezifischen Anforderungen (Interrupt - Atom Problem, etc.)&lt;br /&gt;
erfüllen.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist nur eine Möglichkeit, wie man dies realisieren kann. Sie bietet einem eine relativ einfache Art die EEPROM-Werte&lt;br /&gt;
auf beliebige Adressen zu legen oder Adressen zu ändern. Die Andere Möglichkeit besteht darin, die EEPROM-Werte wie folgt zu belegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
  0x00,                     //  ee_dummy&lt;br /&gt;
  0x05,                     //  ee_value1&lt;br /&gt;
  0x01,                     //  ee_word1L&lt;br /&gt;
  0x00,                     // (ee_word1H)&lt;br /&gt;
  0xFF                      //  ee_value2&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei kann man Variablen, die größer sind als 1 Byte einfacher definieren und&lt;br /&gt;
man muss nur die Highbyte- oder Lowbyte-Adresse in der &amp;quot;eeprom.h&amp;quot; definieren.&lt;br /&gt;
Allerdings muss man hier höllisch aufpassen, dass man nicht um eine oder mehrere&lt;br /&gt;
Positionen verrutscht!&lt;br /&gt;
&lt;br /&gt;
Welche der beiden Möglichkeiten man einsetzt, hängt vor allem davon ab, wieviele&lt;br /&gt;
Byte, Word und sonstige Variablen man benutzt. Gewöhnen sollte man sich an beide Varianten können ;)&lt;br /&gt;
&lt;br /&gt;
Kleine Schlussbemerkung:&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc unterstützt die Variante 1 und die Variante 2&lt;br /&gt;
* Der icc-avr Compiler unterstützt nur die Variante 2!&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Bei alten Versionen der avr-libc wurden nicht alle AVR Controller  untersützt. Z.B. bei der avr-libc Version 1.2.3 insbesondere bei AVRs &amp;quot;der neuen Generation&amp;quot; (ATmega48/88/168/169) funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register). In neueren Versionen (z.B. avr-libc 1.4.3 aus WinAVR 20050125) wurde die Zahl der unterstüzten Controller deutlich erweitert und eine Methode zur leichten Anpassung an zukünftige Controller eingeführt.&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Will oder kann man nicht auf die neue Version aktualisieren, kann der dort gezeigte Code auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;, gegebenfalls Schutz vor Unterbrechnung/Interrupt ergänzen &#039;&#039;uint8_t sreg; sreg=SREG; cli(); [EEPROM-Code] ; SREG=sreg; return;&#039;&#039;, siehe Abschnitt Interrupts). Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== EEPROM Register ===&lt;br /&gt;
Um das EEPROM anzusteuern sind drei Register von Bedeutung.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEAR Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL da in einem 8 Bit Register keine 512 Adressen adressiert werden können&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEDR Hier werden die Daten eingetragen die geschrieben werden sollen bzw. es enthält die gelesenen Daten&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EECR Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TABLE BORDER=1&amp;gt;&lt;br /&gt;
&amp;lt;TR&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;Bit&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;1&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;0&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&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;Name&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&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;TD&amp;gt;-&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERIE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEMWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERE&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;Read/Write&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;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&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;Init Value&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;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;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;TD&amp;gt;0&amp;lt;/TD&amp;gt;&lt;br /&gt;
&amp;lt;/TR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/TABLE&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bedeutung der Bits:&amp;lt;/U&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4-7 nicht belegt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 3 (EERIE) - EEPROM Ready Interrupt Enable:&amp;lt;/U&amp;gt; Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7) wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0 wird kein Interrupt ausgelöst.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 2(EEMWE) - EEPROM Master Write Enable:&amp;lt;/U&amp;gt; Dieses Bit bestimmt, daß wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE =0 ist und EEWE = 1 gesetzt wird hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst.&lt;br /&gt;
Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 1 (EEWE) - EEPROM Write Enable:&amp;lt;/U&amp;gt; Dieses Bit löst den Schreibvorgang aus wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist wird dieses Bit automatisch wieder auf 0 gesetzt und sofern EERIE gesetzt ist ein Interrupt ausgelöst. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ein Schreibvorgang sieht typischerweise wie folgt aus:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. EEPROM Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Daten übergeben an EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1 &amp;lt;BR&amp;gt;&lt;br /&gt;
5. (Optinal) Warten bis Schreibvorgang abgeschlossen ist.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 0 EERE – EEPROM Read Enable:&amp;lt;/U&amp;gt; Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit wenn das Bit EEWE=0 ist. ISt der LEsevorgang abgeschlossen wird das Bit wieder auf 0 gesetzt und das EEPROM ist für neue Lese/Schreibbefehle wieder bereit.&amp;lt;BR&amp;gt;&lt;br /&gt;
Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. Bereitschaft zum lesen prüfen (EEWE=0)&amp;lt;BR&amp;gt;&lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Lesezyklus auslösen mit EERE = 1&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Warten bis Lesevorgang abgeschlossen EERE = 0&amp;lt;BR&amp;gt;&lt;br /&gt;
5. Daten abholen aus EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bietet sich printf/sprintf an. &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t s[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( s, &amp;quot;Zählerstand: %d&amp;quot;, value );&lt;br /&gt;
    uart_puts( s );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, die Ausgabe stdout auf eigene Ausgabefunktionen umzuleiten. Dazu wird dem Ausgabemechanismus eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben (wie auch immer die Ausgabe stattfindet). Alle anderen, höheren, Funktionen greifen letztendlich auf diese eine Ausgabefunktion zurück, die die Ausgabe eines einzelnen Zeichens vornimmt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uart_init();&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
    printf( &amp;quot;Hello, world!\n&amp;quot; );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil aller printf-Varianten: Sie sind recht speicherintensiv.&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C- und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gnu-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Inline-Assembler: Die Assembleranweisungen werden direkt in den C-Code integriert. Eine Quellcode-Datei enthält somit C- und Assembleranweisungen&lt;br /&gt;
* Assembler-Dateien: Der Assemblercode befindet sich in eigenen Quellcodedateien. Diese werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung (NOP steht für No Operation). Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=Warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
   /* Verzögern der weiteren Programmausführung um&lt;br /&gt;
      genau 3 Taktzyklen */&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeintlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
&lt;br /&gt;
    /* leere Schleife - wird bei eingeschalteter Compiler-Optimierung   wegoptimiert */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* alternative Methode (keine Optimierung): */&lt;br /&gt;
    volatile uint16_t j;&lt;br /&gt;
    for (j = 0; j &amp;lt; 1000; j++)&lt;br /&gt;
      ;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützlicher &amp;quot;Assembler-Einzeiler&amp;quot; ist der Aufruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;);&#039;&#039;), da hierzu in älteren Versionen der avr-libc keine eigene Funktion existiert (in neueren Versionen &#039;&#039;sleep_cpu()&#039;&#039; aus sleep.h).&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise Delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil, dass die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static inline void delayloop16 (uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
    asm volatile (&amp;quot;cp  %A0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;cpc %B0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;breq 2f               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;1:                    \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;sbiw %0,1             \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;brne 1b               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;2:                    &amp;quot;  &lt;br /&gt;
                  : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
	          : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
    );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Anweisung wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen. Der Zeilenumbruch teilt dem Assembler mit, dass ein neuer Befehl beginnt.&lt;br /&gt;
* Als Sprung-Marken (Labels) werden Ziffern verwendet. Diese speziellen Labels sind mehrfach im Code verwendbar. Gesprungen wird jeweils zurück (b) oder vorwärts (f) zum nächsten ausffindbaren Label.&lt;br /&gt;
&lt;br /&gt;
Das Resultat zeigt ein Blick in die Assembler-Datei, die der Compiler mit der option &amp;lt;tt&amp;gt;-save-temps&amp;lt;/tt&amp;gt; nicht löscht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	cp  r24, __zero_reg__ 	 ;  count&lt;br /&gt;
	cpc r25, __zero_reg__ 	 ;  count&lt;br /&gt;
	breq 2f               &lt;br /&gt;
	1:                    &lt;br /&gt;
	sbiw r24,1             	 ;  count&lt;br /&gt;
	brne 1b               &lt;br /&gt;
	2:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc im Abschnitt [http://www.nongnu.org/avr-libc/user-manual/inline_asm.html Related Pages/Inline Asm]. &lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc Deutsche Einführung in Inline-Assembler]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
Wenn man mit dem AVR Studio arbeitet, kann alternativ auch das standardmäßig erstellte Makefile bearbeitet und folgende Zeilen eingefügt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
## Objects that must be built in order to link&lt;br /&gt;
OBJECTS = (alte Dateien...) useful.o&lt;br /&gt;
&lt;br /&gt;
## Compile&lt;br /&gt;
## Hier folgt eine Liste der gelinkten Dateien, darunter einfügen:&lt;br /&gt;
useful.o: ../useful.S&lt;br /&gt;
	$(CC) $(INCLUDES) $(ASMFLAGS) -c  $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Das war es schon. Allerdings gilt es zu beachten, dass das makefile über &amp;quot;Project -&amp;gt; Configuration options&amp;quot; selbst einzubinden ist, sonst wird es natürlich wieder überschrieben.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel eine Funktion &#039;&#039;superFunc&#039;&#039;, die alle Pins des Ports D auf &amp;quot;Ausgang&amp;quot; schaltet, eine Funktion &#039;&#039;ultraFunc&#039;&#039;, die die Ausgänge entsprechend des übergebenen Parameters schaltet, eine Funktion &#039;&#039;gigaFunc&#039;&#039;, die den Status von Port A zurückgibt und eine Funktion &#039;&#039;addFunc&#039;&#039;, die zwei Bytes zu einem 16-bit-Wort addiert. Die Zuweisungen im C-Code (PORTx = ...) verhindern, dass der Compiler die Aufrufe wegoptimiert und dienen nur zur Veranschaulichung der Parameterübergaben.&lt;br /&gt;
&lt;br /&gt;
Zuerst der Assembler-Code. Der Dateiname sei useful.S:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//; Arbeitsregister (ohne &amp;quot;r&amp;quot;) &lt;br /&gt;
workreg  = 16&lt;br /&gt;
workreg2 = 17&lt;br /&gt;
&lt;br /&gt;
//; Konstante:&lt;br /&gt;
ALLOUT = 0xff&lt;br /&gt;
&lt;br /&gt;
//; ** Setze alle Pins von PortD auf Ausgang **&lt;br /&gt;
//; keine Parameter, keine Rückgabe&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg  // beachte: _SFR_IO_ADDR()&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Setze PORTD auf übergebenen Wert **&lt;br /&gt;
//; Parameter in r24 (LSB immer bei &amp;quot;graden&amp;quot; Nummern)&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zustand von PINA zurückgeben **&lt;br /&gt;
//; Rückgabewerte in r24:r25 (LSB:MSB), hier nur LSB genutzt&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zwei Bytes addieren und 16-bit-Wort zurückgeben **&lt;br /&gt;
//; Parameter in r24 (Summand1) und r22 (Summand2) -&lt;br /&gt;
//;  Parameter sind Word-&amp;quot;aligned&amp;quot; d.h. LSB immer auf &amp;quot;graden&amp;quot;&lt;br /&gt;
//;  Registernummern. Bei 8-Bit und 16-Bit Paramtern somit &lt;br /&gt;
//;  beginnend bei r24 dann r22 dann r20 etc.&lt;br /&gt;
//; Rückgabewert in r24:r25&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   push workreg2&lt;br /&gt;
   clr workreg2&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
   pop workreg2&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
//; oh je - sorry - Mein AVR-Assembler ist eingerostet, hoffe das stimmt so...&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Makefile ist der Name der Assembler-Quellcodedatei einzutragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
ASRC = useful.S&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Aufruf erfolgt dann im C-Code so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
extern void superFunc(void);&lt;br /&gt;
extern void ultraFunc(uint8_t setVal);&lt;br /&gt;
extern uint8_t gigaFunc(void);&lt;br /&gt;
extern uint16_t addFunc(uint8_t w1, uint8_t w2);&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
[...]&lt;br /&gt;
  superFunc();&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
&lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
[...]&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird wieder in der lss-Datei ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
   superFunc();&lt;br /&gt;
 148:	0e 94 f6 00 	call	0x1ec&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
 14c:	85 e5       	ldi	r24, 0x55	; 85&lt;br /&gt;
 14e:	0e 94 fb 00 	call	0x1f6&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
 152:	0e 94 fd 00 	call	0x1fa&lt;br /&gt;
 156:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
  &lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
 158:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 15a:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 15c:	0e 94 ff 00 	call	0x1fe&lt;br /&gt;
 160:	8b bb       	out	0x1b, r24	; 27&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
 162:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 164:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 166:	0e 94 fc 00 	call	0x1f8&lt;br /&gt;
 16a:	89 2f       	mov	r24, r25&lt;br /&gt;
 16c:	99 27       	eor	r25, r25&lt;br /&gt;
 16e:	88 bb       	out	0x18, r24	; 24&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
000001ec &amp;lt;superFunc&amp;gt;:&lt;br /&gt;
// setze alle Pins von PortD auf Ausgang&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1ec:	0f 93       	push	r16&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
 1ee:	0f ef       	ldi	r16, 0xFF	; 255&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg&lt;br /&gt;
 1f0:	01 bb       	out	0x11, r16	; 17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 1f2:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 1f4:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001f6 &amp;lt;ultraFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// setze PORTD auf übergebenen Wert&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
 1f6:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
   ret&lt;br /&gt;
 1f8:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fa &amp;lt;gigaFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Zustand von PINA zurückgeben&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
 1fa:	89 b3       	in	r24, 0x19	; 25&lt;br /&gt;
   ret&lt;br /&gt;
 1fc:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fe &amp;lt;addFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// zwei Bytes addieren und 16-bit-Wort zurückgeben&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1fe:	0f 93       	push	r16&lt;br /&gt;
   push workreg2&lt;br /&gt;
 200:	1f 93       	push	r17&lt;br /&gt;
   clr workreg2&lt;br /&gt;
 202:	11 27       	eor	r17, r17&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
 204:	06 2f       	mov	r16, r22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
 206:	08 0f       	add	r16, r24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
 208:	11 1d       	adc	r17, r1&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
 20a:	c8 01       	movw	r24, r16&lt;br /&gt;
   pop workreg2&lt;br /&gt;
 20c:	1f 91       	pop	r17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 20e:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 210:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zuweisung von Registern zu Parameternummer und die Register für die Rückgabewerte sind in den &amp;quot;Register Usage Guidelines&amp;quot; der avr-libc-Dokumentation erläutert.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/assembler.html avr-libc-Dokumentation: Related Pages/avr-libc and assembler programs]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage avr-libc-Dokumentation: Related Pages/FAQ/&amp;quot;What registers are used by the C compiler?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
== Globale Variablen für Datenaustausch ==&lt;br /&gt;
&lt;br /&gt;
Oftmals kommt man um globale Variablen nicht herum, z.B. um den Datenaustausch zwischen Hauptprogramm und Interrupt-Routinen zu realisieren. &lt;br /&gt;
Hierzu muss man im Assembler wissen, wo genau die Variable vom C-Compiler abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
Hierzu muss die Variable, hier &amp;quot;zaehler&amp;quot; genannt, zuerst im C-Code als Global definiert werden, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
int16_t main (void)&lt;br /&gt;
{&lt;br /&gt;
    // irgendein Code, in dem zaehler benutzt werden kann&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im foldenden Assembler-Beispiel wird der Externe Interrupt0  verwendet, um den Zähler hochzuzählen. Es fehlen die Initialisierungen des Interrupts und die Interrupt-Freigabe, so richtig sinnvoll ist der Code auch nicht, aber er zeigt (hoffentlich) wie es geht.&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit Interrupt-Vektoren gilt beim GCC-Assembler das Gleiche, wie bei C: Man muss die exakte Schreibweise beachten, ansonsten wird nicht der Interrupt-Vektor angelegt, sondern eine neue Funktion - und man wundert sich, dass nichts funktionert (vgl. das AVR-GCC-Handbuch).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
.extern zaehler&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      //; wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     //; Status-Register (SREG) sichern!&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     lds temp,zaehler               //; Wert aus dem Speicher lesen&lt;br /&gt;
     inc temp                       //; bearbeiten&lt;br /&gt;
     sts zaehler,temp               //; und wieder zurückschreiben&lt;br /&gt;
&lt;br /&gt;
     pop temp                       //; die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen im Assemblerfile anlegen ===&lt;br /&gt;
&lt;br /&gt;
Alternativ können Variablen aber auch im Assemblerfile angelegt werden. Dadurch kann auf eine .c-Datei verzichtet werden. Für das obige Beispiel könnte der Quelltext dann die Dateien zaehl_asm.S und zaehl_asm.h abgelegt werden, so dass nur noch zaehl_asm.S mit kompiliert werden müsste.&lt;br /&gt;
&lt;br /&gt;
Anstatt im Assemblerfile über das Schlüsselwort &#039;&#039;.extern &#039;&#039; auf eine vorhandene Variable zu verweisen, wird dazu mit dem Schlüsselwort &#039;&#039;.comm&#039;&#039; die benötigte Anzahl von Bytes für eine Variable reserviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.S&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
//; 1 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 1&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Headerdatei wird dann auf die Variable nur noch verwiesen (Schlüsselwort &#039;&#039;extern&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef ZAEHL_ASM_H&lt;br /&gt;
#define ZAEHL_ASM_H&lt;br /&gt;
&lt;br /&gt;
extern volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu globalen Variablen in C werden so angelegte Variablen nicht automatisch mit dem Wert 0 initialisiert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer als 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Variablen, die größer als &#039;&#039;&#039;ein&#039;&#039;&#039; Byte sind, können in Assembler auf ähnliche Art angesprochen werden. Hierzu müssen nur genug Bytes angefordert werden, um die Variable aufzunehmen. Soll z.B. für den Zähler eine Variable vom Typ &#039;&#039;unsigned long&#039;&#039;, also &#039;&#039;uint32_t&#039;&#039; verwendet werden, so müssen 4 Bytes reserviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die dazugehörige Deklaration im Headerfile wäre dann:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
extern volatile uint32_t zaehler;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen, die größer als ein Byte sind, werden die Werte beginnend mit dem niederwertigsten Byte im RAM abgelegt. Das folgende Codeschnippsel zeigt, wie unter Assembler auf die einzelnen Bytes zugegriffen werden kann. Dazu wird im Interrupt nun ein 32-Bit Zähler erhöht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      // wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     // Status-Register (SREG) sichern !&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     // 32-Bit-Zähler incrementieren&lt;br /&gt;
     lds temp, (zaehler + 0)        // 0. Byte (niederwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 0), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
     lds temp, (zaehler + 1)        // 1. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 1), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 2)        // 2. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 2), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 3)        // 3. Byte (höchstwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 3), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
RAUS:&lt;br /&gt;
     pop temp                       // die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TODO:&#039;&#039;&#039; 16-Bit / 32-Bit Variablen, Zugriff auf Arrays (Strings)&lt;br /&gt;
&lt;br /&gt;
= Anhang =&lt;br /&gt;
&lt;br /&gt;
== Besonderheiten bei der Anpassung bestehenden Quellcodes ==&lt;br /&gt;
&lt;br /&gt;
Einige Funktionen, die in frühren Versionen der avr-libc vorhanden waren, werden inzwischen als veraltet angesehen. Sie sind nicht mehr vorhanden oder als &#039;&#039;deprecated&#039;&#039; (missbilligt) ausgewiesen und Definitionen in &amp;lt;compat/deprecated.h&amp;gt; verschoben. Es empfiehlt sich, vorhandenen Code zu portieren und die alten Funktionen nicht mehr zu nutzen, auch wenn diese noch zur Verfügung stehen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zur Deklaration von Interrupt-Routinen ===&lt;br /&gt;
&lt;br /&gt;
Die Funktionen (eigentlich Makros) &#039;&#039;SIGNAL&#039;&#039; und &#039;&#039;INTERRUPT&#039;&#039; zur Deklaration von Interruptroutinen sollten nicht mehr genutzt werden. &lt;br /&gt;
&lt;br /&gt;
In aktuellen Versionen der avr-libc (z.B. avr-libc 1.4.3 aus WinAVR 20060125) werden Interruptroutinen, die &#039;&#039;&#039;nicht&#039;&#039;&#039; durch andere Interrupts &#039;&#039;&#039;unterbrechbar&#039;&#039;&#039; sind, mit ISR deklariert (siehe Abschnitt im Hauptteil). Auch die Benennung wurden vereinheitlicht und an die üblichen Bezeichnungen in den AVR Datenblättern angepasst. In der Dokumentation der avr-libc sind alte und neue Bezeichnungen in der Tabelle gegenübergestellt. Die erforderlichen Schritte zur Portierung:&lt;br /&gt;
&lt;br /&gt;
* #include von avr/signal.h entfernen&lt;br /&gt;
* SIGNAL duch ISR ersetzen&lt;br /&gt;
* Name des Interrupt-Vektors anpassen (SIG_* durch entsprechendes *_vect)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für die Anpassung zuerst ein &amp;quot;alter&amp;quot; Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer2 Output Compare bei einem ATmega8 */&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE2)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt wird der Vektor mit TIMER2 COMP bezeichnet. Die Bezeichnung in der avr-libc entspricht dem Namen im Datenblatt, Leerzeichen werden durch Unterstriche (_) ersetzt und ein _vect angehängt. &lt;br /&gt;
&lt;br /&gt;
Der neue Code sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt; &lt;br /&gt;
/* signal.h entfällt */&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER2_COMP_vect)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Unklarheiten bezüglich der neuen Vektorlabels hilft (noch) ein Blick in die Headerdatei des entsprechenden Controllers. Für das vorherige Beispiel also der Blick in die Datei iom8.h für den ATmega8, dort findet man die veraltete Bezeichnung unterhalb der aktuellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect		_VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2		_VECTOR(3)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Nachfolgendes mit avr-libc 1.4.5 (in WinAVR 1/2007 behoben - noch eine Weile auskommentiert lassen und dann löschen.&lt;br /&gt;
Konnte in alten Versionen signal.h ohne interrupt.h eingebunden werden, erhält man bei Verwendung der avr-libc Version 1.4.3 (WinAVR 2/2005) beim Compilieren eine Fehlermeldung, da mit signal.h nicht die erforderlichen Definitionen eingebunden werden. Der Lösungsvorschlag in signal.h auch interrupt.h einzubinden, wurde von den avr-libc-Enwicklern akzeptiert und das Problem ist  im Quellcode (CVS) bereits behoben. Es ist aber noch keine avr-libc-&amp;quot;Release&amp;quot; bzw. noch kein WinAVR mit dieser avr-libc-Korrektur verfügbar (Stand 5.2.2006). Will oder kann man den Quellcode nicht aktualisieren, gibt es folgende Alternativen:&lt;br /&gt;
* in Quellcodedateien, in denen nur avr/signal.h eingebunden wird, interrupt.h einbinden (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;). signal.h weiterhin einbinden, falls Kompatibiltät mit alten Versionen gewünscht ist.&lt;br /&gt;
* in der Datei signal.h (bein WinAVR in c:/WinAVR/avr/include/avr/signal.h) ein (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;) ergänzen --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;unterbrechbare&#039;&#039;&#039; Interruptroutinen, die mittels &#039;&#039;INTERRUPT&#039;&#039; deklariert sind, gibt es keinen direkten Ersatz in Form eines Makros. Solche Routinen sind laut Dokumentation der avr-libc in folgender Form zu deklarieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* ** alt ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
INTERRUPT(SIG_OVERFLOW0)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ** neu: ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void TIMER0_OVF_vect(void) __attribute__((interrupt));&lt;br /&gt;
void TIMER0_OVF_vect(void) &lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von &#039;&#039;INTERRUPT&#039;&#039; die Header-Datei &#039;&#039;compat/deprecated.h&#039;&#039; einzubinden. Man sollte bei dieser Gelegenheit jedoch nochmals überprüfen, ob die Funktionalität von &#039;&#039;INTERRUPT&#039;&#039; tatsächlich gewollt ist. In vielen Fällen wurde &#039;&#039;INTERRUPT&#039;&#039; dort genutzt, wo eigentlich &#039;&#039;SIGNAL&#039;&#039; (nunmehr &#039;&#039;ISR&#039;&#039;) hätte genutzt werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;inp&#039;&#039; und &#039;&#039;outp&#039;&#039; zum Einlesen bzw. Schreiben von Registern sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
unsigned char i, j;&lt;br /&gt;
&lt;br /&gt;
// alt:&lt;br /&gt;
  i = inp(PINA);&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  outp(PORTB, j);&lt;br /&gt;
&lt;br /&gt;
// neu (nicht mehr wirklich neu...):&lt;br /&gt;
  i = PINA&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  PORTB = j;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von inp und outp die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Zugriff auf Bits in Registern ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;cbi&#039;&#039; und &#039;&#039;sbi&#039;&#039; zum Löschen und Setzen von Bits sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg. Die Bezeichnung ist ohnehin irreführend da die Funktionen nur für Register mit Adressen im unteren Speicherbereich tatsächlich in die Assembleranweisungen cbi und sbi übersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// alt:&lt;br /&gt;
  sbi(PORTB, PB2);&lt;br /&gt;
  cbi(PORTC, PC1);&lt;br /&gt;
&lt;br /&gt;
// neu (auch nicht mehr wirklich neu...):&lt;br /&gt;
  PORTB |=  (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
  PORTC &amp;amp;= ~(1&amp;lt;&amp;lt;PC1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von sbi und cbi die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden. Wer unbedingt will, kann sich natürlich eigene Makros mit aussagekräftigeren Namen definieren. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &amp;amp;= ~(1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Selbstdefinierte (nicht-standardisierte) ganzzahlige Datentypen ===&lt;br /&gt;
&lt;br /&gt;
Bei den im Folgenden genannten Typdefinitionen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich, bei der Überarbeitung von altem Code die im Abschnitt &#039;&#039;standardisierten ganzzahligen Datentypen&#039;&#039; beschriebenen Datentypen zu nutzen (stdint.h) und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef unsigned char      BYTE;       // besser: uint8_t  aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned short     WORD;       // besser: uint16_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long      DWORD;      // besser: uint32_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long long QWORD;      // besser: uint64_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; BYTE : Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 255. &lt;br /&gt;
&lt;br /&gt;
; WORD : Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 65535. &lt;br /&gt;
&lt;br /&gt;
; DWORD : Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
; QWORD : Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Funktionen im Makefile ==&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken (Libraries/.a-Dateien) hinzufügen ===&lt;br /&gt;
&lt;br /&gt;
Um Funktionen aus Bibliotheken (&amp;quot;echte&amp;quot; Libraries, *.a-Dateien) zu nutzen, sind dem Linker die Namen der Bibliotheken als Parameter zu übergeben. Dazu ist die Option -l (kleines L) vorgesehen, an die der Name der Library angehängt wird. &lt;br /&gt;
&lt;br /&gt;
Dabei ist zu beachten, dass der Name der Library und der Dateiname der Library nicht identisch sind. Der hinter -l angegebene Name entspricht dem Dateinamen der Library ohne die Zeichenfolge &#039;&#039;lib&#039;&#039; am Anfang des Dateinamens und ohne die Endung &#039;&#039;.a&#039;&#039;. Sollen z.B. Funktionen aus einer Library mit dem Dateinamen &#039;&#039;libefsl.a&#039;&#039; eingebunden (gelinkt) werden, lautet der entsprechende Parameter -lefsl (vergl. auch -lm zum Anbinden von libm.a). &lt;br /&gt;
&lt;br /&gt;
In Makefiles wird traditonell eine make-Variable LDLIBS genutzt, in die &amp;quot;l-Parameter&amp;quot; abgelegt werden. Die WinAVR-makefile-Vorlage enthält diese Variable zwar nicht, dies stellt jedoch keine Einschränkung dar, da alle in der make-Variable LDFLAGS abgelegten Parameter an den Linker weitergereicht werden. &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Einbinden von Funktionen aus einer Library efsl (Dateiname libefsl.a)&lt;br /&gt;
LDFLAGS += -lefsl&lt;br /&gt;
# Einbinden von Funktionen aus einer Library xyz (Dateiname libxyz.a)&lt;br /&gt;
LDFLAGS += -lxyz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Liegen die Library-Dateien nicht im Standard Library-Suchpfad, sind die Pfade mittels Parameter &#039;&#039;-L&#039;&#039; ebenfalls anzugeben. (Der vordefinierte Suchpfad kann mittels &#039;&#039;avr-gcc --print-search-dirs&#039;&#039; angezeigt werden.)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Projekt (&amp;quot;superapp2&amp;quot;), in dem der Quellcode von zwei Libraries (efsl und xyz) und der Quellcode der eigentlichen Anwendung in verschiedenen Verzeichnissen mit der folgenden &amp;quot;Baumstruktur&amp;quot; abgelegt sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
superapp2&lt;br /&gt;
|&lt;br /&gt;
+----- efslsource (darin libefsl.a)&lt;br /&gt;
|&lt;br /&gt;
+----- xyzsource (darin libxyz.a)&lt;br /&gt;
|&lt;br /&gt;
+----- firmware (darin Anwendungs-Quellcode und Makefile)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt, dass im Makefile die Verzeichnis efslsource und xyzsource in den Library-Suchpfad aufzunehmen sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
LDFLAGS += -L../efslsource/ -L../xyzsource/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fuse-Bits ===&lt;br /&gt;
&lt;br /&gt;
Zur Berechnung der Fuse-Bits bietet sich neben dem Studium des Datenblattes auch der [http://palmavr.sourceforge.net/cgi-bin/fc.cgi AVR Fuse Calculator] an. Gewarnt werden muss vor der Benutzung von PonyProg, weil dort durch die negierte Darstellung gern Fehler gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Soll die Programmierung von Fuse- und Lockbits automatisiert werden, kann man dies ebenfalls durch Einträge im Makefile vornehmen, die beim Aufruf von &amp;quot;make program&amp;quot; an die genutzte Programmiersoftware übergeben werden. In der makefile-Vorlage von WinAVR (und mfile) gibt es dafuer jedoch keine &amp;quot;Ausfüllhilfe&amp;quot; (Stand 9/2006). Die folgenden Ausführungen gelten für die Programmiersoftware [[AVRDUDE]] (Standard in der WinAVR-Vorlage), können jedoch sinngemäß auf andere Programmiersoftware übertragen werden, die die Angabe der Fuse- und Lockbits-Einstellungen per Kommandozeilenparameter unterstützt (z.B. stk500.exe). Im einfachsten Fall ergänzt man im Makefile einige Variablen, deren Werte natürlich vom verwendeten Controller und den gewünschten Einstellungen abhängen (vgl. Datenblatt Fuse-/Lockbits):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#---------------- Programming Options (avrdude) ----------------&lt;br /&gt;
&lt;br /&gt;
#...&lt;br /&gt;
#Beispiel! f. ATmega16 - nicht einfach uebernehmen! Zahlenwerte anhand&lt;br /&gt;
#--------- des Datenblatts nachvollziehen und gegebenenfalls aendern.&lt;br /&gt;
#&lt;br /&gt;
AVRDUDE_WRITE_LFUSE = -U lfuse:w:0xff:m&lt;br /&gt;
AVRDUDE_WRITE_HFUSE = -U hfuse:w:0xd8:m&lt;br /&gt;
AVRDUDE_WRITE_LOCK  = -U lock:w:0x2f:m&lt;br /&gt;
#...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit diese Variablen auch genutzt werden, ist der Aufruf von avrdude im Makefile entsprechend zu ergänzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Program the device.  &lt;br /&gt;
program: $(TARGET).hex $(TARGET).eep&lt;br /&gt;
# ohne Fuse-/Lock-Einstellungen (nach WinAVR Vorlage Stand 4/2006)&lt;br /&gt;
#	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
#        $(AVRDUDE_WRITE_EEPROM)&lt;br /&gt;
# mit Fuse-/Lock-Einstellungen&lt;br /&gt;
        $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_LFUSE) \&lt;br /&gt;
        $(AVRDUDE_WRITE_HFUSE) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
        $(AVRDUDE_WRITE_EEPROM) $(AVRDUDE_WRITE_LOCK)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weiter Möglichkeit besteht darin, die Fuse- und Lockbit-Einstellungen vom Preprozessor/Compiler generieren zu lassen. Die Fuse-Bits werden dann bei Verwendung von AVRDUDE in eigene Hex-Files geschrieben. Hierzu kann man z.B. folgendes Konstrukt verwenden:&lt;br /&gt;
&lt;br /&gt;
In eine der C-Sourcen wird eine Variable je Fuse-Byte vom Typ &#039;&#039;unsigned char&#039;&#039; deklariert und in eine extra Section gepackt. Dies kann entweder in einem vorhandenen File passieren oder in ein neues (z.B. fuses.c) geschrieben werden. Das File muss im Makefile aber auf jeden Fall mit kompiliert und gelinkt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// tiny 2313 fuses low byte&lt;br /&gt;
#define CKDIV8  7&lt;br /&gt;
#define CKOUT   6&lt;br /&gt;
#define SUT1    5&lt;br /&gt;
#define SUT0    4&lt;br /&gt;
#define CKSEL3  3&lt;br /&gt;
#define CKSEL2  2&lt;br /&gt;
#define CKSEL1  1&lt;br /&gt;
#define CKSEL0  0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses high byte&lt;br /&gt;
#define DWEN       7&lt;br /&gt;
#define EESAVE     6&lt;br /&gt;
#define SPIEN      5&lt;br /&gt;
#define WDTON      4&lt;br /&gt;
#define BODLEVEL2  3&lt;br /&gt;
#define BODLEVEL1  2&lt;br /&gt;
#define BODLEVEL0  1&lt;br /&gt;
#define RSTDISBL   0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses extended byte&lt;br /&gt;
#define SELFPRGEN  0&lt;br /&gt;
&lt;br /&gt;
#define LFUSE         __attribute__ ((section (&amp;quot;lfuses&amp;quot;)))&lt;br /&gt;
#define HFUSE         __attribute__ ((section (&amp;quot;hfuses&amp;quot;)))&lt;br /&gt;
#define EFUSE         __attribute__ ((section (&amp;quot;efuses&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// select ext crystal 3-8Mhz&lt;br /&gt;
unsigned char lfuse LFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;CKDIV8) | (1&amp;lt;&amp;lt;CKOUT) | (1&amp;lt;&amp;lt;CKSEL3) | (1&amp;lt;&amp;lt;CKSEL2) | &lt;br /&gt;
      (0&amp;lt;&amp;lt;CKSEL1) | (1&amp;lt;&amp;lt;CKSEL0) | (0&amp;lt;&amp;lt;SUT1) | (1&amp;lt;&amp;lt;SUT0) );&lt;br /&gt;
unsigned char hfuse HFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;DWEN) | (1&amp;lt;&amp;lt;EESAVE) | (0&amp;lt;&amp;lt;SPIEN) | (1&amp;lt;&amp;lt;WDTON) | &lt;br /&gt;
      (1&amp;lt;&amp;lt;BODLEVEL2) | (1&amp;lt;&amp;lt;BODLEVEL1) | (0&amp;lt;&amp;lt;BODLEVEL0) | (1&amp;lt;&amp;lt;RSTDISBL) );&lt;br /&gt;
unsigned char efuse EFUSE =&lt;br /&gt;
    ((0&amp;lt;&amp;lt;SELFPRGEN));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG: Die Bitpositionen wurden nicht vollständig getestet!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Eine &amp;quot;1&amp;quot; bedeutet hier, dass das Fuse-Bit &#039;&#039;nicht&#039;&#039; programmiert wird - die Funktion also i.A. nicht aktiviert ist. Eine &amp;quot;0&amp;quot; hingegen aktiviert die meisten Funktionen. Dies ist wie im Datenblatt (1 = unprogrammed, 0 = programmed).&lt;br /&gt;
&lt;br /&gt;
Das Makefile muss nun noch um folgende Targets erweitert werden (mit Tabulator einrücken - nicht mit Leerzeichen):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
lfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j lfuses --change-section-address lfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-lfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-lfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U lfuse:w:$(TARGET)-lfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
hfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j hfuses --change-section-address hfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-hfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-hfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U hfuse:w:$(TARGET)-hfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
efuses: build&lt;br /&gt;
        -$(OBJCOPY) -j efuses --change-section-address efuses=0 \&lt;br /&gt;
         -O ihex $(TARGET).elf $(TARGET)-efuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-efuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U efuse:w:$(TARGET)-efuse.hex;&lt;br /&gt;
        fi;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Target &amp;quot;clean&amp;quot; muss noch um die Zeilen&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
        $(REMOVE) $(TARGET)-lfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-hfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-efuse.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
erweitert werden, wenn auch die Fuse-Dateien gelöscht werden sollen.&lt;br /&gt;
&lt;br /&gt;
Um nun die Fusebits des angeschlossenen Controllers zu programmieren muss lediglichein &amp;quot;make lfuses&amp;quot;, &amp;quot;make hfuses&amp;quot; oder &amp;quot;make efuses&amp;quot; gestartet werden.&lt;br /&gt;
Bei den Fuse-Bits ist besondere Vorsicht geboten, da diese das Programmieren des Controllers unmöglich machen können. Also erst programmieren, wenn man einen HV-Programmierer hat oder ein paar Reserve-AVRs zur Hand ;-)&lt;br /&gt;
&lt;br /&gt;
Um weiterhin den &amp;quot;normalen&amp;quot; Flash beschreiben zu können, ist es wichtig, für das Target &amp;quot;*.hex&amp;quot; im Makefile nicht nur &amp;quot;-R .eeprom&amp;quot; als Parameter zu übergeben sondern zusätzlich noch &amp;quot;-R lfuses -R efuses -R hfuses&amp;quot;. Sonst bekommt AVRDUDE Probleme diese Sections in den Flash (wo sie ja nicht hingehören) zu schreiben.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[AVR_Fuses#Vergleich_der_Fuses_bei_verschiedenen_Programmen|Vergleich der Fuses bei verschiedenen Programmen]]&lt;br /&gt;
&lt;br /&gt;
== Externe Referenzspannung des internen Analog-Digital-Wandlers ==&lt;br /&gt;
&lt;br /&gt;
Die minimale (externe) Referenzspannung des ADC darf nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. z.B. beim ATMEGA8 darf sie laut Datenblatt (S.245, Tabelle 103, Zeile &amp;quot;VREF&amp;quot;) 2,0V nicht unterschreiten. HINWEIS: diese Information findet sich erst in der letzten Revision (Rev. 2486O-10/04) des Datenblatts.&lt;br /&gt;
&lt;br /&gt;
Meiner &amp;lt;!-- Wer? - es gibt inzwischen x Leute die mehr oder weniger viel in diesem Artikel geschrieben haben --&amp;gt; eigenen Erfahrung nach kann man aber (auf eigene Gefahr und natürlich nicht für Seriengeräte) durchaus noch ein klein wenig weiter heruntergehen, bei dem von mir unter die Lupe genommenen ATMEGA8L (also die Low-Voltage-Variante) funktioniert der ADC bei 5V Betriebsspannung mit bis zu VREF=1,15V hinunter korrekt, ab 1,1V und darunter digitalisiert er jedoch nur noch Blödsinn). Ich würde sicherheitshalber nicht unter 1,5V gehen und bei niedrigeren Betriebsspannungen mag sich die Untergrenze für VREF am Pin AREF ggf. nach oben&#039;&#039;&#039;(!)&#039;&#039;&#039; verschieben.&lt;br /&gt;
&lt;br /&gt;
In der letzten Revision des Datenblatts ist außerdem korrigiert, dass ADC4 und ADC5 sehr wohl 10 Bit Genauigkeit bieten (und nicht bloß 8 Bit, wie in älteren Revisionen irrtümlich angegeben.)&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* stdio.h, malloc() &lt;br /&gt;
* Code-Optimierungen (&amp;quot;tricks&amp;quot;), siehe auch Application Note [http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf AVR035: Efficient C Coding for AVR]&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
* SPI siehe [http://www.uni-koblenz.de/~physik/informatik/MCU/SPI.pdf SPI Bus mit Atmel AVR]&lt;br /&gt;
* I²C / TWI Bus [http://www.roboternetz.de/wissen/index.php/TWI]&lt;br /&gt;
* Bootloader (bez. auf boot.h)&lt;br /&gt;
* CAN-Bus&lt;br /&gt;
* Einsatz von einfachen Betriebssystemen auf dem AVR&lt;br /&gt;
* Übersicht zu den C bzw. GCC-predefined Makros (__DATE__, __TIME__,...)&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
* ADC ; &lt;br /&gt;
* Timer&lt;br /&gt;
* USB ; Steuerung mit USB&lt;br /&gt;
* Multiplexen Siebensegment&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28255</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28255"/>
		<updated>2008-05-27T17:11:13Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Bitfelder */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[AVR-GCC]] erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Progammiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong]. Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern, weder in Assembler noch in einer anderen Sprache. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR Controllern finden. Bei WinAVR gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden stetig weiterentwickelt. Erläuterungen und Beispiele beziehen sich auf den C-Compiler avr-gcc ab Version 3.4 und die avr-libc ab Version 1.4.3. Die Unterschiede zu älteren Versionen werden im Haupttext und Anhang zwar erläutert, Anfängern sei jedoch empfohlen, die aktuellen Versionen zu nutzen (für MS-Windows: aktuelle Version des [[WinAVR]]-Pakets). &lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in PDF-Form erhältlich bei:&lt;br /&gt;
http://www.siwawi.arubi.uni-kl.de/avr_projects/AVR-GCC-Tutorial_-_www_mikrocontroller_net.pdf&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels avr-gcc/avr-libc zu erstellen und zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR Controllers, der vom avr-gcc Compiler unterstützt wird (alle ATmegas und die meisten AT90, siehe Dokumentation der avr-libc für unterstützte Typen). Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man [[AVR#Starterkits|hier]].&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc Compiler und die avr-libc. Kostenlos erhältlich für nahezu alle Plattformen und Betriebssysteme. Für MS-Windows im Paket [[WinAVR]]; für Unix/Linux siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]]).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder nur die eigenen C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
&lt;br /&gt;
* Die [[AVR Checkliste]] durcharbeiten.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/2 GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  auf den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
&lt;br /&gt;
* Die Beispieleprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweisen sind aber auch auf C-Programme übertragbar.&lt;br /&gt;
&lt;br /&gt;
* Einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien, um das Problem nachzuvollziehen, sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z. B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.&amp;amp;nbsp;B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.&amp;amp;nbsp;B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen, um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu zwei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer integrierten Entwicklungsumgebung (IDE = &#039;&#039;&#039;I&#039;&#039;&#039;ntegrated &#039;&#039;&#039;D&#039;&#039;&#039;evelopment &#039;&#039;&#039;E&#039;&#039;&#039;nvironment), bei der alle Einstellungen z.&amp;amp;nbsp;B. in Dialogboxen durchgeführt werden können. Unter Anderem kann AVRStudio ab Version 4.12 (kostenlos auf [http://www.atmel.com/ atmel.com]) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs für den avr-gcc (ohne Anspruch auf Vollständigkeit): AtmanAvr C (relativ günstig), KamAVR (kostenlos), VMLab (ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.&amp;amp;nbsp;B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand der universellen und plattformunabhängigen Vorgehensweise mittels make und Makefiles näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind lediglich Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xff;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5a)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/;  // (5b)&lt;br /&gt;
   }                         // (5c)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (6)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In der mit (1) markierten Zeile wird eine so genannte Header-Datei eingebunden. In io.h sind die Registernamen definiert, die im späteren Verlauf genutzt werden.&lt;br /&gt;
&lt;br /&gt;
* Bei (2) beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion main.&lt;br /&gt;
&lt;br /&gt;
* Die Anschlüsse eines AVR (&amp;quot;Beinchen&amp;quot;) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Richtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B zu Ausgängen.&lt;br /&gt;
&lt;br /&gt;
* (4) stellt die Werte der Ausgänge ein. Die den ersten beiden Bits des Ports zugeordneten Anschlüsse (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential).&lt;br /&gt;
&lt;br /&gt;
* (5) ist die so genannte Hauptschleife (main-loop). Dies ist eine Programmschleife, welche kontinuierlich wiederkehrende Befehle enthält. In diesem Beispiel ist sie leer. Der Controller durchläuft die Schleife immer wieder, ohne dass etwas passiert (außer das Strom verbraucht wird). Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Ohne diese Schleife wäre der Zustand des Controllers nach dem Programmende undefiniert.&lt;br /&gt;
&lt;br /&gt;
* (6) wäre das Programmende. Die Zeile ist nur aus Gründen der C-Kompatibilität enthalten: int main(void) besagt, dass die Funktion einen Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein auf dem Controller lauffähiges Programm zu übersetzen, wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen Makefile (ohne Endung) im selben Verzeichnis, in dem auch die Datei main.c mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im folgenden Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\tmp\gcc_tut\quickstart&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei main.hex, in der der Code für den AVR enthalten ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.&amp;amp;nbsp;B. über In-System-Programming (ISP) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann (z.&amp;amp;nbsp;B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio), kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine LED leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode mit einem dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Exkurs: Makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebungen à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Plattformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt, genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.com) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das Makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den im Makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im Makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten Makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt [[AVR-GCC-Tutorial#EEPROM|Speicherzugriffe]]), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendeten Controllers gesetzt. Eine Liste der von avr-gcc und der avr-libc unterstützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien eintragen ==&lt;br /&gt;
&lt;br /&gt;
Der Name der Quellcodedatei, welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.&amp;amp;nbsp;B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
# nicht auskommentiert: EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Das erstellte Makefile und der Code müssen im gleichen Ordner sein, auch sollte der Dateiname nicht verändert werden. &lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem Makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschinencode erzeugt. Beim Update auf eine neue Compilerversion ist zu beachten, dass diese möglicherweise intern andere Optimierungsalgorithmen verwendet und sich dadurch die Größe des Machinencodes etwas ändert, ohne dass man im Quellcode etwas geändert hat.&lt;br /&gt;
&lt;br /&gt;
Als Orientierungswerte die Größe des Maschinencodes bei verschiedenen Optionen für einen nicht näher spezifizierten relativ kleinen Testcode bei Verwendung einer nicht näher spezifizierten Compilerversion. &lt;br /&gt;
&lt;br /&gt;
* -O0 : 12&#039;217 Byte&lt;br /&gt;
&lt;br /&gt;
* -O1 : 9&#039;128 Byte&lt;br /&gt;
&lt;br /&gt;
* -O2 : 1&#039;670 Byte&lt;br /&gt;
&lt;br /&gt;
* -O3 : 3&#039;004 Byte&lt;br /&gt;
&lt;br /&gt;
* -Os : 1&#039;695 Byte&lt;br /&gt;
&lt;br /&gt;
Im diesem Testfall führt die Option -O2 mit zum kompaktesten Code, dies  allerdings hier nur mit 25 Bytes &amp;quot;Vorsprung&amp;quot;. Es kann durchaus sein, dass nur wenige Programmerweiterungen dazu führen, dass Compilieren mit -Os wieder in kompakteren Code resultiert.&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/using_tools.html#gcc_optO avr-libc manual Abschnitt Using the gnu-tools/Compiler-Optionen]&lt;br /&gt;
&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_optflags avr-libc Manual FAQ Nr. 16] (Stand avr-libc Version 1.4.5)&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wird hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (großes S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten, lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Taktfrequenz ===&lt;br /&gt;
&lt;br /&gt;
Neuere Versionen der WinAVR/Mfile Vorlage für Makefiles beinhalten die Definition einer Variablen F_CPU (WinAVR 2/2005). Darin wird die Taktfrequenz des Controllers in Hertz eingetragen. Die Definition steht dann im gesamten Projekt ebenfalls unter der Bezeichnung F_CPU zur Verfügung (z.&amp;amp;nbsp;B. um daraus UART-, SPI- oder ADC-Frequenzeinstellungen abzuleiten).&lt;br /&gt;
&lt;br /&gt;
Die Angabe hat rein &amp;quot;informativen&amp;quot; Charakter, die tatsächliche Taktrate wird über den externen Takt (z.&amp;amp;nbsp;B. Quarz) bzw. die Einstellung des internen R/C-Oszillators  bestimmt. Die Nutzung von F_CPU hat also nur Sinn, wenn die Angabe mit dem tatsächlichen Takt übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Innerhalb neuerer Versionen der avr-libc (ab Version 1.2) wird die Definition der Taktfrequenz (F_CPU) zur Berechnung der Wartefunktionen in delay.h genutzt. Diese funktionieren nur dann korrekt, wenn F_CPU mit der tatsächlichen Taktfrequenz übereinstimmt.&lt;br /&gt;
F_CPU muss dazu jedoch nicht unbedingt im makefile definiert werden. Es reicht aus, wird aber bei mehrfacher Anwendung unübersichtlich, vor &#039;&#039;#include &amp;lt;util/delay.h&amp;gt;&#039;&#039; (veraltet: &#039;&#039;#include &amp;lt;avr/delay.h&amp;gt;&#039;&#039;) ein &#039;&#039;#define F_CPU [hier Takt in Hz]UL&#039;&#039; einzufügen. Bei Nutzung von delay.h ist darauf zu achten, dass die Optimierung des Compilers nicht ausgeschaltet ist, sonst wird sehr viel Code erzeugt und die Wartezeit stimmt nicht mit der gewünschten Zeitspanne überein. Vgl. dazu den [http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html entsprechenden Abschnitt der Dokumentation].&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage so genannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* Im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
* Die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* Weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff: (sollte nur noch in Ausnahmefällen genutzt werden)&lt;br /&gt;
&lt;br /&gt;
* Im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
* Die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* Weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler diese &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, einem Nachbau davon (BootICE, Evertool o.&amp;amp;nbsp;ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
Die Verwendung von Makefiles bietet noch viele weitere Möglichkeiten, einige davon werden im Anhang [[AVR-GCC-Tutorial#Zus.C3.A4tzliche_Funktionen_im_Makefile|Zusätzliche Funktionen im Makefile]] erläutert.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige (Integer) Datentypen =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei stdint.h definiert. &lt;br /&gt;
Zur Nutzung der standardisierten Typen bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// ab avr-libc Version 1.2.0 möglich und empfohlen:&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// veraltet: #include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef signed char        int8_t;&lt;br /&gt;
typedef unsigned char      uint8_t;&lt;br /&gt;
&lt;br /&gt;
typedef short              int16_t;&lt;br /&gt;
typedef unsigned short     uint16_t;&lt;br /&gt;
&lt;br /&gt;
typedef long               int32_t;&lt;br /&gt;
typedef unsigned long      uint32_t;&lt;br /&gt;
&lt;br /&gt;
typedef long long          int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* int8_t steht für einen 8-Bit Integer mit einem Wertebereich -128 bis +127.&lt;br /&gt;
&lt;br /&gt;
* uint8_t steht für einen 8-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 255&lt;br /&gt;
&lt;br /&gt;
* int16_t steht für einen 16-Bit Integer mit einem Wertebereich -32768 bis +32767.&lt;br /&gt;
&lt;br /&gt;
* uint16_t steht für einen 16-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 65535.&lt;br /&gt;
&lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; werden als vorzeichenbehaftete Zahlen abgespeichert. Typen mit vorgestelltem &#039;&#039;u&#039;&#039; dienen der Ablage von postiven Zahlen (inkl. 0). Siehe dazu auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/(Standard) Integer Types.&lt;br /&gt;
&lt;br /&gt;
= Bitfelder =&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bits breit ist.&lt;br /&gt;
&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in eine einzelne Bytevariable zusammenfassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können. Die Rede ist von so genannten Bitfeldern. Diese werden als Strukturelemente definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitfelder sparen Platz im RAM, zu Lasten von Platz im Flash, verschlechtern aber unter Umständen die Les- und Wartbarkeit des Codes. Anfängern wird deshalb geraten, ein &amp;quot;ganzes&amp;quot; Byte (uint8_t) zu nutzen, auch wenn nur ein Bitwert gespeichert werden soll.&lt;br /&gt;
&lt;br /&gt;
Wenn man nur ein paar wenige Variablen vom Typ bool verwenden möchte, kann man&lt;br /&gt;
auch die Headerdatei &amp;lt;stdbool.h&amp;gt; einbinden und sich dann wie gewohnt einen Booltyp anlegen. Variablen dieses Typs brauchen dennoch 1 Byte Speicher, ermöglichen aber eine genaue Unterscheidung zwischen Zahlenvariable und boolscher Variable.&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;C-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum können für allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden&lt;br /&gt;
AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039;, wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output).  Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt.&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend&lt;br /&gt;
angepasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O-Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek (avr-libc) stellt auch Funktionen zur Abfrage eines einzelnen Bits eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist, wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind &#039;&#039;nicht erforderlich&#039;&#039;, man kann auch &amp;quot;einfache&amp;quot; C-Syntax verwenden, die universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) wenn das Bit gesetzt und 0 (&amp;quot;falsch&amp;quot;) wenn es nicht gesetzt ist. Die Negierung des Ausdrucks, also  &#039;&#039;&#039;!&#039;&#039;&#039;(Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)), entspricht &#039;&#039;bit_is_clear&#039;&#039; und gibt einen Wert &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) zurück, falls das Bit nicht gesetzt ist,&lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O-Register einfach wie eine Variable setzen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und outp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben von Bits ====&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausdruck:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit (für 1) eines Bytes hat die Bitnummer 0, das &amp;quot;höchstwertige&amp;quot; (für 128) die Nummer 7.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );    /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3);   /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die warten, bis ein bestimmter Zustand auf einem Bit erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend gewartet&amp;quot; wird. D.h., der Programmablauf bleibt an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den Watchdog ein, muss man darauf achten, dass dieser auch noch getriggert wird (zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Änderung: Habe PINx und PORTx vertauscht.PINx ist für Pull up und PORTx für high bzw low. Michael.L  - &lt;br /&gt;
&lt;br /&gt;
Was sollte diese Aenderung?? Den Rest des Abschnitts auch gelesen? Evtl. die hier nicht erlaeuterten Zusatzfunktion der neuen AVRs durcheinandergeworfen? Aenderung verworfen bis mehr Details.  m.thomas. &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
// Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
&lt;br /&gt;
DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
/* mehr Tipparbeit aber übersichtlicher: */&lt;br /&gt;
DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&lt;br /&gt;
/* Einige Compiler erlauben die Eingabe von Konstanten im &lt;br /&gt;
   Binärformat, z.B. gcc in WinAVR, NICHT aber aus&lt;br /&gt;
   GNU-gcc-Quellen selbst erstellte Compiler. Diese Schreib-&lt;br /&gt;
   weise also nicht nutzen, wenn Code mit anderen &lt;br /&gt;
   ausgetauscht werden soll. */&lt;br /&gt;
DDRB = 0b00011111;    /* direkte Zuweisung &lt;br /&gt;
                         - übersichtlicher &lt;br /&gt;
                         - nicht standardkonform &lt;br /&gt;
                         - selten portabel&lt;br /&gt;
                         - nur bei modifiziertem GCC */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
Weitere Beispiele:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
DDRB = 0xff; &lt;br /&gt;
// Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; DDB0 );&lt;br /&gt;
// Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( ( 1 &amp;lt;&amp;lt; DDB3 ) | ( 1&amp;lt;&amp;lt;DDB4) );&lt;br /&gt;
// Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB |= ( 1 &amp;lt;&amp;lt; DDB0) | ( 1 &amp;lt;&amp;lt; DDB3 );&lt;br /&gt;
// Alle Pins auf Eingang:&lt;br /&gt;
DDRB = 0x00;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&#039;&#039;&lt;br /&gt;
* &#039;&#039;zuerst die Bits im PORTx-Register setzen und&#039;&#039;&lt;br /&gt;
* &#039;&#039;anschließend die Datenrichtung auf Ausgang stellen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Daraus ergibt sich die Abfolge für einen Eingangspin:&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze PORTx: interner Pull-Up aktiv&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze DDRx: Ausgang &amp;quot;high&amp;quot;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bei der Reihenfolge erst DDRx und dann PORTx, kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;&#039;nicht&#039;&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, &#039;&#039;&#039;sondern&#039;&#039;&#039; Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.&amp;lt;/font&amp;gt; (Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände.)&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch.  Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVRs haben sogar an den meisten Pins softwaremäßig zuschaltbare interne Pull-Up Widerstände, welche wir natürlich auch verwenden können.&lt;br /&gt;
&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;DDC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== (Tasten-)Entprellung ====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim Schließen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&lt;br /&gt;
&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen als mehrfache Impulse gezählt wird. Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* bei alter avr-libc: #include &amp;lt;avr/delay.h&amp;gt; */      &lt;br /&gt;
&lt;br /&gt;
/* Einfache Funktion zum Entprellen eines Tasters */&lt;br /&gt;
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)&lt;br /&gt;
{&lt;br /&gt;
    if ( ! (*port &amp;amp; (1 &amp;lt;&amp;lt; pin)) )&lt;br /&gt;
    {&lt;br /&gt;
        /* Pin wurde auf Masse gezogen, 100ms warten   */&lt;br /&gt;
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz&lt;br /&gt;
        _delay_ms(50); &lt;br /&gt;
        if ( *port &amp;amp; (1 &amp;lt;&amp;lt; pin) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Anwender Zeit zum Loslassen des Tasters geben */&lt;br /&gt;
            _delay_ms(50);&lt;br /&gt;
            _delay_ms(50); &lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; PB0 );                 /* PIN PB0 auf Eingang (Taster)            */&lt;br /&gt;
    PORTB |= ( 1 &amp;lt;&amp;lt; PB0 );                 /* Pullup-Widerstand aktivieren            */&lt;br /&gt;
    ...&lt;br /&gt;
    if (debounce(&amp;amp;PINB, PB0))             /* Falls Taster an PIN PB0 gedrueckt..    */&lt;br /&gt;
        PORTD = PIND ^ ( 1 &amp;lt;&amp;lt; PD7 );  /* ..LED an Port PD7 an-&lt;br /&gt;
                                   bzw. ausschalten */&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesem Beispiel ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritische Anwendungen sollte man ein anderes Verfahren nutzen (z.B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).&lt;br /&gt;
&lt;br /&gt;
Zum Thema Entprellen siehe auch:&lt;br /&gt;
* Artikel [[Entprellung]]&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls ben&amp;amp;ouml;tigt, kann eine 16-Bit Variable auch recht einfach manuell in ihre zwei 8-Bit Bestandteile zerlegt werden. Folgendes Beispiel demonstriert dies anhand des pseudo- 16-Bit Registers UBRR.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
typedef union {&lt;br /&gt;
        uint16_t i16;&lt;br /&gt;
        struct {&lt;br /&gt;
                uint8_t i8l;&lt;br /&gt;
                uint8_t i8h;&lt;br /&gt;
        };&lt;br /&gt;
} convert16to8;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
convert16to8 baud;&lt;br /&gt;
baud.i16 = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
UBRRH = baud.i8h;&lt;br /&gt;
UBRRL = baud.i8l;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/*alternativ:*/&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint16_t wFoo16;&lt;br /&gt;
uint8_t bFooLow, bFooHigh;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
wFoo16   = 0xAA55;                 /* zu &amp;quot;zerlegende&amp;quot; 16Bit-Integer */&lt;br /&gt;
bFooHigh = (uint8_t)(wFoo16 &amp;gt;&amp;gt; 8); /* MS-Byte */&lt;br /&gt;
bFooLow  = (uint8_t)(wFoo16);      /* LS-Byte */&lt;br /&gt;
...&lt;br /&gt;
#define us0(Data) ((unsigned char *)(&amp;amp;Data))&lt;br /&gt;
#define us1(Data) ((unsigned char *)((&amp;amp;Data)+1))&lt;br /&gt;
bFooHigh = us1(wFoo16);&lt;br /&gt;
bFoolow  = us0(wFoo16);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen (z.B. ATmega8) teilen sich UBRRH und UCSRC die gleiche Memory-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL) welches Register tats&amp;amp;auml;chlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und &amp;amp;uuml;bergibt den Wert &#039;&#039;3&#039;&#039; und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und &amp;amp;uuml;bergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Bei einigen 16-bit Registern (insbesondere die der 16-bit Timer) erfolgen Schreibzugriffe über das sogenannte &#039;&#039;temporary Register&#039;&#039;. Die Reihenfolge der Zugriffe bestimmt, wann der Wert tatsächlich ins Register geschrieben wird. Typisch wird erst das High-Byte beschrieben (xxxH) und intern im temporary Register zwischengespeichert. Nachdem das Low-Byte (xxxL) geschrieben wurde, setzt der Controller mit diesem und dem im temporary Register zwischengespeicherten Wert für das High-Byte das 16-bit Register. Dabei ist zu beachten, dass intern nur ein temporary Register verfügbar ist, welches in Interruptroutinen mglw. mit einem anderen Wert überschrieben wird, wenn dort ebenfalls 16-bit Register beschrieben werden. avr-gcc/avr-libc berücksichtigen die korrekte Reihenfolge automatisch, wenn die Register mit ihrem &amp;quot;16-bit Label&amp;quot; (ohne H bzw. L) angesprochen werden, dabei ist der Schutz des temporary Registers vor Überschreiben durch Interruptroutinen dennoch zu beachten (im Zweifel beim Schreibzugriff die Interrupts kurzzeitig global deaktivieren).&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t key_pressed(const volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if ( last_state == ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) ) ) {&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    i = key_pressed( &amp;amp;PINB, PB1 );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Im Übrigen können mit volatile gekennzeichnete Variablen auch als const deklariert werden, um sicherzustellen, dass sie nur noch von der Hardware änderbar sind.&lt;br /&gt;
&lt;br /&gt;
= Der UART =&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Über den [[UART]] kann ein AVR leicht mit einer [[RS-232]]-Schnittstelle eines PC oder sonstiger Geräte mit &amp;quot;serieller Schnittstelle&amp;quot; verbunden werden. &lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungen des UART:&lt;br /&gt;
&lt;br /&gt;
* Debug-Schnittstelle: z.B. zur Anzeige von Zwischenergebnissen (&amp;quot;printf-debugging&amp;quot; - hier besser &amp;quot;UART-debugging&amp;quot;) auf einem PC. Auf dem Rechner reicht dazu ein [[RS-232#Terminalprogramme|Terminalprogramm]] (MS-Windows: Hyperterm oder besser Bray-Terminal, [http://www.mikrocontroller.net/forum/read-8-155472.html#new HTerm]; Unix/Linux z.B. minicom). Ein direkter Anschluss ist aufgrund unterschiedlicher Pegel nicht möglich, jedoch sind entsprechende Schnittstellen-ICs wie z.B. ein MAX232 günstig und leicht zu integrieren. Rechner ohne serielle Schnittstelle können über fertige USB-seriell-Adapter angeschlossen werden. &lt;br /&gt;
* &amp;quot;Mensch-Maschine Schnittstelle&amp;quot;: z.B. Konfiguration und Statusabfrage über eine &amp;quot;Kommandozeile&amp;quot; oder Menüs (siehe z.B. Forumsbeitrag [http://www.mikrocontroller.net/topic/52985 Auswertung RS232-Befehle]) &lt;br /&gt;
* Übertragen von gespeicherten Werten: z.B. bei einem Datenlogger&lt;br /&gt;
* Anschluss von Geräten mit serieller Schnittstelle (z.B. (Funk-)Modems, Mobiltelefone, Drucker, Sensoren, &amp;quot;intelligente&amp;quot; LC-Displays, GPS-Empfänger). &lt;br /&gt;
* &amp;quot;Feldbusse&amp;quot; auf RS485/RS422-Basis mittels entsprechenden Bustreiberbausteinen (z.B. MAX485)&lt;br /&gt;
* DMX, Midi etc.&lt;br /&gt;
* LIN-Bus (&#039;&#039;&#039;L&#039;&#039;&#039;ocal &#039;&#039;&#039;I&#039;&#039;&#039;nterconnect &#039;&#039;&#039;N&#039;&#039;&#039;etwork): Preiswerte Sensoren/Aktoren in der Automobiltechnik und darüber hinaus&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben ein bis zwei vollduplexfähigen UART (&#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) schon eingebaut (&amp;quot;Hardware-UART&amp;quot;). &lt;br /&gt;
Übrigens: Vollduplex heißt nichts anderes, als dass der Baustein gleichzeitig senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega, ATtiny) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterscheidet sich vom UART hauptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurationsregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit zeigt an, ob der Sendepuffer bereit ist, um ein zu sendendes Zeichen aufzunehmen. Das Bit wird vom AVR gesetzt (1), wenn der Sendepuffer leer ist. Es wird gelöscht (0), wenn ein Zeichen im Sendedatenregister vorhanden ist und noch nicht in das Sendeschieberegister übernommen wurde. Atmel empfiehlt aus Kompatibilitätsgründen mit kommenden µC, UDRE auf 0 zu setzen, wenn das UCSRA Register beschrieben wird.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
\mathrm{UBRR} = \frac{\mathrm{Taktfrequenz}}{\mathrm{Baudrate} \cdot 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS-232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. &lt;br /&gt;
&amp;lt;!-- &amp;quot;Hackerloesung&amp;quot; auskommentiert - nicht so gut in einem &amp;quot;Einsteiger-Tutorial&amp;quot; - mthomas&lt;br /&gt;
Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfiehlt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== UART initialisieren ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben. Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach gewünschter Funktionsweise die benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Da wir vorerst nur senden möchten und noch keine Interrupts auswerten wollen, gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs mit USART haben mehrere Konfigurationsregister und erfordern eine etwas andere Konfiguration. Für einen ATmega16 z.B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                 // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);     // Asynchron 8N1&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist noch das Baudratenregister &#039;&#039;&#039;UBRR&#039;&#039;&#039; einzustellen. Bei neueren AVRs besteht es aus zwei Registern &#039;&#039;&#039;UBRRL&#039;&#039;&#039; und &#039;&#039;&#039;UBRRH&#039;&#039;&#039;. Der Wert dafür ergibt sich aus der angegebenen Formel durch Einsetzen der Taktfrequenz und der gewünschten Übertragungsrate. Das Berechnen der Formel wird dem [[C-Präprozessor|Präprozessor]] überlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann&lt;br /&gt;
   F_CPU im Makefile definiert werden, eine nochmalige Definition&lt;br /&gt;
   hier wuerde zu einer Compilerwarnung fuehren. Daher &amp;quot;Schutz&amp;quot; durch&lt;br /&gt;
   #ifndef/#endif &lt;br /&gt;
&lt;br /&gt;
   Dieser &amp;quot;Schutz&amp;quot; kann zu Debugsessions führen, wenn AVRStudio &lt;br /&gt;
   verwendet wird und dort eine andere, nicht zur Hardware passende &lt;br /&gt;
   Taktrate eingestellt ist: Dann wird die folgende Definition &lt;br /&gt;
   nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) &lt;br /&gt;
   von AVRStudio - daher Ausgabe einer Warnung falls F_CPU&lt;br /&gt;
   noch nicht definiert: */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000&amp;quot;&lt;br /&gt;
#define F_CPU 4000000UL    // Systemtakt in Hz &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600UL          // Baudrate&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) // Fehler in Promille, 1000 = kein Fehler.&lt;br /&gt;
&lt;br /&gt;
#if ((BAUD_ERROR&amp;lt;990) || (BAUD_ERROR&amp;gt;1010))&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;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    UBRR = UBRR_VAL;&lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&amp;lt;!-- mthomas: warum nicht UL?, wird von AVRStudio auch mit UL übergeben --&amp;gt;&lt;br /&gt;
Wieder für den Mega16 mit zwei Registern für die Baudrateneinstellung eine etwas andere Programmierung. Wichtig ist, dass UBRRH &#039;&#039;&#039;vor&#039;&#039;&#039; UBRRL geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmega16 */&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einige AVR (z.B. ATmega169, ATmega48/88/168, AT90CAN jedoch nicht für z.B. ATmega16/32, ATmega128, ATtiny2313) wird durch die Registerdefinitionen der avr-libc (io*.h) auch für Controller mit zwei UBRR-Registern (UBRRL/UBRRH) ein UBRR bzw. UBRR0 als &amp;quot;16-bit-Register&amp;quot; definiert und man kann auch Werte direkt per UBRR = UBRR_VAL zuweisen. Intern werden dann zwei Zuweisungen für UBRRH und UBRRL generiert. Dies ist nicht bei allen Controllern möglich, da die beiden Register nicht bei allen aufeinanderfolgende Addressen aufweisen. Die getrennte Zuweisung an UBRRH und UBRRL wie im Beispiel gezeigt ist universeller und portabler und daher vorzuziehen.&lt;br /&gt;
&lt;br /&gt;
Die Makros sind sehr praktisch, da sie sowohl automatisch den Wert für UBRR als auch den Fehler in der generierten Baudrate berechnen und im Falle einer Überschreitung (+/-1%) einen Fehler und somit Abbruch im Compilerablauf generieren. Damit können viele Probleme mit &amp;quot;UART sendet komische Zeichen&amp;quot; vermieden werden. Ausserdem kann man mühelos die Einstellung an eine neue Taktfrequenz bzw. Baudrate anpassen, ohne selber rechnen oder in Tabellen nachschlagen zu müssen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. Vorher ist zu prüfen, ob das UART-Modul bereit ist, das zu sendende Zeichen entgegenzunehmen. Die Bezeichnungen des/der Statusregisters mit dem Bit UDRE ist abhängig vom Controllertypen (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    // bei AVR mit einem UART (&amp;quot;classic AVR&amp;quot; z.B. AT90S8515)&lt;br /&gt;
    while (!(USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                  /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&lt;br /&gt;
    /** ODER **/&lt;br /&gt;
&lt;br /&gt;
    // bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                    /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe &amp;quot;String senden&amp;quot; wird durch zwei Funktionen abgearbeitet. Die universelle/controllerunabhängige Funktion uart_puts übergibt jeweils ein Zeichen der Zeichenkette an eine Funktion uart_putc, die abhängig von der vorhandenen Hardware implementiert werden muss. In der Funktion zum Senden eines Zeichens ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// putc fuer AVR mit einem UART (z.B. AT90S8515)&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while(!(USR &amp;amp; (1 &amp;lt;&amp;lt; UDRE)))  /* warte, bis UDR bereit */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = c;                     /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** ODER **/&lt;br /&gt;
&lt;br /&gt;
// bei neueren AVRs andere Bezeichnung fuer die Statusregister, hier ATmega16:&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich */&lt;br /&gt;
    {&lt;br /&gt;
    }                             &lt;br /&gt;
&lt;br /&gt;
    UDR = c;                      /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* puts ist unabhaengig vom Controllertyp */&lt;br /&gt;
void uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != &#039;\0&#039; also ungleich dem &amp;quot;String-Endezeichen&amp;quot; */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die in uart_putc verwendeten Schleifen, in denen gewartet wird bis die UART-Hardware zum senden bereit ist, sind insofern etwas kritisch, da während des Sendens eines Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. empfangenen Zeichen/Bytes zwischengespeichert und in Interruptroutinen an die U(S)ART-Hardware weitergegeben bzw. von ihr ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&lt;br /&gt;
&lt;br /&gt;
=== Schreiben von Variableninhalten ===&lt;br /&gt;
&lt;br /&gt;
Sollen Inhalte von Variablen (Ganzzahlen, Fließkomma) in &amp;quot;menschenlesbarer&amp;quot; Form gesendet werden, ist vor dem Transfer eine Umwandlung in Zeichen (&amp;quot;ASCII&amp;quot;) erforderlich. Bei nur einer Ziffer ist diese Umwandlung relativ einfach: man addiert den ASCII-Wert von Null zur Ziffer und kann diesen Wert direkt senden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_putc (s.o.)&lt;br /&gt;
&lt;br /&gt;
// Ausgabe von 0123456789&lt;br /&gt;
char c;&lt;br /&gt;
&lt;br /&gt;
for (uint8_t i=0; i&amp;lt;=9; ++i) {&lt;br /&gt;
    c = i + &#039;0&#039;;&lt;br /&gt;
    uart_putc( c );&lt;br /&gt;
    // verkuerzt: uart_putc( i + &#039;0&#039; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll mehr als eine Ziffer ausgegeben werden, bedient man sich zweckmäßigerweise vorhandener Funktionen zur Umwandlung von Zahlen in Zeichenketten/Strings. Die Funktion der avr-libc zur Umwandlung von vorzeichenbehafteten 16bit-Ganzzahlen (int16_t) in Zeichenketten heißt &#039;&#039;itoa&#039;&#039; (Integer to ASCII). Man muss der Funktion einen Speicherbereich zur Verarbeitung (buffer) mit Platz für alle Ziffern, das String-Endezeichen (&#039;\0&#039;) und evtl. das Vorzeichen bereitstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   char s[7];&lt;br /&gt;
   int16_t i = -12345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   itoa( i, s, 10 ); // 10 fuer radix -&amp;gt; Dezimalsystem&lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // da itoa einen Zeiger auf den Beginn von s zurueckgibt verkuerzt auch:&lt;br /&gt;
   uart_puts( itoa( i, s, 10 ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für vorzeichenlose 16bit-Ganzzahlen (uint16_t) exisitert &#039;&#039;utoa&#039;&#039;. Die Funktionen für 32bit-Ganzzahlen (int32_t und uint32_t) heißen &#039;&#039;ltoa&#039;&#039; bzw. &#039;&#039;ultoa&#039;&#039;. Da 32bit-Ganzzahlen mehr Stellen aufweisen können, ist ein entsprechend größerer Pufferspeicher vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Auch Fließkommazahlen (float/double) können mit breits vorhandenen Funktionen in Zeichenfolgen umgewandelt werden, dazu existieren die Funktionen &#039;&#039;dtostre&#039;&#039; und &#039;&#039;dtostrf&#039;&#039;. dtostre nutzt Exponentialschreibweise (&amp;quot;engineering&amp;quot;-Format). (Hinweis: z.Zt. existiert im avr-gcc kein &amp;quot;echtes&amp;quot; double, intern wird immer mit &amp;quot;einfacher Genauigkeit&amp;quot;, entsprechend float, gerechnet.) &lt;br /&gt;
&lt;br /&gt;
dtostrf und dtostre benötigen die libm.a der avr-libc. Bei Nutzung von Makefiles ist der Parameter -lm in in LDFLAGS anzugeben (Standard in den WinAVR/mfile-Makefilevorlagen). Nutzt man AVRStudio als IDE für den GNU-Compiler (gcc-Plugin) ist die libm.a unter Libaries auszuwählen: Project -&amp;gt; Configurations Options -&amp;gt; Libaries -&amp;gt; libm.a mit dem Pfeil nach rechts einbinden. Siehe auch die [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|FAQ]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
/* lt. avr-libc Dokumentation:&lt;br /&gt;
char* dtostrf(&lt;br /&gt;
  double __val,&lt;br /&gt;
  char   __width,&lt;br /&gt;
  char   __prec,&lt;br /&gt;
  char * __s&lt;br /&gt;
)  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   // Pufferspeicher ausreichend groß&lt;br /&gt;
   // evtl. Vorzeichen + width + Endezeichen:&lt;br /&gt;
   char s[8]; &lt;br /&gt;
   float f = -12.345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   dtostrf( f, 6, 3, s ); &lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // verkürzt: uart_puts( dtostrf( f, 7, 3, s ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfangen ==&lt;br /&gt;
=== einzelne Zeichen empfangen ===&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von Zeichen muss der Empfangsteil des UART bei der Initialisierung aktiviert werden, indem das RXEN-Bit im jeweiligen Konfigurationsregister (UCSRB bzw UCSR0B/UCSR1B) gesetzt wird. Im einfachsten Fall wird solange gewartet, bis ein Zeichen empfangen wurde, dieses steht dann im UART-Datenregister (UDR bzw. UDR0 und UDR1 bei AVRs mit 2 UARTS) zur Verfügung (sogen. &amp;quot;Polling-Betrieb&amp;quot;). Ein Beispiel für den ATmega16:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Zusaetzlich zur Baudrateneinstellung und der weiteren Initialisierung: */&lt;br /&gt;
void Usart_EnableRX()&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= ( 1 &amp;lt;&amp;lt; RXEN );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion blockiert den Programmablauf. Alternativ kann das RXC-Bit in einer Programmschleife abgefragt werden und dann nur bei gesetztem RXC-Bit UDR ausgelesen werden. Eleganter und in den meisten Anwendungsfällen &amp;quot;stabiler&amp;quot; ist die Vorgehensweise, die empfangenen Zeichen in einer Interrupt-Routine einzulesen und zur späteren Verarbeitung in einem Eingangsbuffer (FIFO-Buffer) zwischenzuspeichern. Dazu existieren fertige und gut getestete [[Libraries|Bibliotheken]] &amp;lt;!-- &amp;quot;echte Libraries&amp;quot; (.a) wie im Verweis beschrieben sind hier eigentlich nicht gemeint, verwirrt hier etwas, da AVR-&amp;quot;Libraries&amp;quot; meist per #defines anpassbare Source-Codes sind, vielleicht so: --&amp;gt; und Quellcodekomponenten (z.B. UART-Library von P. Fleury, procyon-avrlib und einige in der &amp;quot;Academy&amp;quot; von avrfreaks.net).&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html Dokumenation der avr-libc/stdlib.h]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_printf Die Nutzung von printf]&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&amp;lt;!-- nimmermehr: * siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (printf etc.) im [[AVR-Tutorial:_UART]]. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: 9bit&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Zeichenketten (Strings) ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Zeichenketten, muß man sich zunächst darüber im klaren sein, daß es ein Kriterium geben muß, an dem der µC erkennen kann, wann ein String zu Ende ist. Sehr oft wird dazu das Zeichen &#039;Return&#039; benutzt, um das Ende eines Strings zu markieren. Dies ist vom Benutzer einfach eingebbar und er ist auch daran gewöhnt, daß er eine Eingabezeile mit einem Druck auf die Return Taste abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Prinzipiell gibt es jedoch keine Einschränkung bezüglich dieses speziellen Zeichens. Es muß nur sichergestellt werden, daß dieses spezielle &#039;Ende eines Strings&#039; - Zeichen nicht mit einem im Text vorkommenden Zeichen verwechselt werden kann. Wenn also im zu übertragenden Text beispielsweise kein &#039;;&#039; vorkommt, dann spricht nichts dagegen, einen String mit einem &#039;;&#039; abschließen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird die durchaus übliche Annahme getroffen, daß eine Stringübertragung identisch ist mit der Übertragung einer Textzeile und daher mit einem Return (&#039;\n&#039;) abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Das Problem der Übertragung eines Strings reduziert sich damit auf die Aufgabenstellung: Empfange und Sammle Zeichen in einem char Array, bis entweder das Array voll ist oder das &#039;String Ende Zeichen&#039; empfangen wurde. Danach wird der empfangene Text noch mit einem &#039;\0&#039; Zeichen abgeschlossen um einen Standard C-String daraus zu machen, mit dem dann weiter gearbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_gets( char* Buffer, uint8_t MaxLen )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t NextChar;&lt;br /&gt;
  uint8_t StringLen = 0;&lt;br /&gt;
&lt;br /&gt;
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen&lt;br /&gt;
&lt;br /&gt;
                                  // Sammle solange Zeichen, bis:&lt;br /&gt;
                                  // * entweder das String Ende Zeichen kam&lt;br /&gt;
                                  // * oder das aufnehmende Array voll ist&lt;br /&gt;
  while( NextChar != &#039;\n&#039; &amp;amp;&amp;amp; StringLen &amp;lt; MaxLen - 1 ) {&lt;br /&gt;
    *Buffer++ = NextChar;&lt;br /&gt;
    StringLen++;&lt;br /&gt;
    NextChar = uart_getc();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
                                  // Noch ein &#039;\0&#039; anhängen um einen Standard&lt;br /&gt;
                                  // C-String daraus zu machen&lt;br /&gt;
  *Buffer = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf ist darauf zu achten, dass das empfangende Array auch mit einer&lt;br /&gt;
vernünftigen Größe definiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
  char Line[40];      // String mit maximal 39 zeichen&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) );&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Interruptbetrieb==&lt;br /&gt;
&lt;br /&gt;
Beim ATMEGA8 ist es möglich, dass RXCIE Bit im Register UCSRB zu setzen.&lt;br /&gt;
Dieser Interrupt wird immer ausglöst, wenn Daten erfolgreich empfangen wurden.&lt;br /&gt;
Zusätzlich braucht man die Routine:&lt;br /&gt;
&lt;br /&gt;
ISR (USART_RXC_vect) {&lt;br /&gt;
   //irgendein Code&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
natürlich muss &amp;quot;Global Interrupt Enable&amp;quot; Aktiviert sein.&lt;br /&gt;
!! Nur getestet beim ATMEGA8 !!&lt;br /&gt;
&lt;br /&gt;
[BAUSTELLE! Aus Lerngründen eventuell als eigenen UART-Interrupt-Block hinter den grundlegenden Interrupt-Teil im Tutorial und hier eine kurze Einführung und einen Verweis darauf anbieten.]&lt;br /&gt;
&lt;br /&gt;
* Unterschied Polling-Betrieb (bisher, oben) und Interrupt-Betrieb&lt;br /&gt;
* Empfangen (Receive)&lt;br /&gt;
** Verändertes UART-Init, ISR (RXC), ggf. Fallstricke ([http://www.mikrocontroller.net/topic/84256#707214 UDR in der ISR lesen!]), Philosophie einer ISR (kurz und schmerzlos), Datenaustausch ISR zu Restprogramm (volatile)&lt;br /&gt;
** Einfachstbeispiel ([http://www.mikrocontroller.net/topic/84228#707052 Echo] (noch buggy beim Datenzugriff, siehe Lit. 2+3!)), ggf. LED zur ISR-Empfangsanzeige oder Overflow-Anzeige&lt;br /&gt;
* FIFO-Puffer, Ringpuffer&lt;br /&gt;
* Senden (Transmit)&lt;br /&gt;
** Variante &amp;quot;UART Data Register Empty&amp;quot; (UDRE) &lt;br /&gt;
** Variante &amp;quot;UART Transmit Complete&amp;quot; (TXC) &lt;br /&gt;
* Fertige UART-Bibliotheken (-&amp;gt;Fleury, -&amp;gt;Procyon)&lt;br /&gt;
* Literatur: &lt;br /&gt;
** 1/ [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=48188 avrfreaks.net Tutorial] inkl. Diskussion (engl.)&lt;br /&gt;
** 2/ avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_16bitio Why do some 16-bit timer registers sometimes get trashed?]&lt;br /&gt;
** 3/ [[Interrupt]] und atomarer Datenzugriff&lt;br /&gt;
&lt;br /&gt;
==Software-UART==&lt;br /&gt;
&lt;br /&gt;
Falls die Zahl der vorhandenen Hardware-UARTs nicht ausreicht, können weitere Schnittstellen über sogennante Software-UARTs ergänzt werden. Es gibt dazu (mindestens) zwei Ansätze: &lt;br /&gt;
* Der bei AVRs üblichste Ansatz basiert auf dem Prinzip, dass ein externer Interrupt-Pin für den Empfang (&amp;quot;RX&amp;quot;) genutzt wird. Das Startbit löst den Interrupt aus, in der Interrupt-Routine (ISR) wird der externe Interrupt deaktiviert und ein Timer aktiviert. In der Interrupt-Routine des Timers wird der Zustand des Empfangs-Pins entsprechend der Baudrate abgetastet. Nach Empfang des Stop-Bits wird der externe Interrupt wieder aktiviert. Senden kann über einen beliebigen Pin (&amp;quot;TX&amp;quot;) erfolgen, der entsprechend der Baudrate und dem zu sendenden Zeichen auf 0 oder 1 gesetzt wird. Die Implementierung ist nicht ganz einfach, es existieren dazu aber fertige Bibliotheken (z.B. bei [http://www.avrfreaks.net/ avrfreaks] oder in der [http://hubbard.engr.scu.edu/embedded/avr/avrlib/ Procyon avrlib]).&lt;br /&gt;
* Ein weiterer Ansatz erfordert keinen Pin mit &amp;quot;Interrupt-Funktion&amp;quot; aber benötigt mehr Rechenzeit. Jeder Input-Pin kann als Empfangspin (RX) dienen. Über einen Timer wird der Zustand des RX-Pins mit einem vielfachen der Baudrate abgetastet (dreifach scheint üblich) und High- bzw. Lowbits anhand einer Mindestanzahl identifiziert. (Beispiel: &amp;quot;Generic Software Uart&amp;quot; Application-Note von IAR)&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (z.B. ATtiny26 oder ATmega48,88,168,169) verfügen über ein Universal Serial Interface (USI), das teilweise UART-Funktion übernehmen kann. Atmel stellt eine Application-Note bereit, in der die Nutzung des USI als UART erläutert wird (im Prinzip &amp;quot;Hardware-unterstützter Software-UART&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==Handshaking==&lt;br /&gt;
Wenn der Sender ständig sendet, wird irgendwann der Fall eintreten, daß der Empfänger nicht bereit ist, neue Zeichen zu empfangen. In diesem Fall muß durch ein &#039;&#039;&#039;Handshake-Verfahren&#039;&#039;&#039; die Situation bereinigt werden. Handshake bedeutet nichts anderes, als daß der Empfänger dem Sender mitteilt, daß er zur Zeit keine Daten annehmen kann und der Sender die Übertragung der nächsten Zeichen solange einstellen soll, bis der Empfänger signalisiert, daß er wieder Zeichen aufnehmen kann.&lt;br /&gt;
===Hardwarehandshake (RTS/CTS)===&lt;br /&gt;
Beim Hardwarehandshake werden zusätzlich zu den beiden Daten-Übertragungsleitungen noch 2 weitere Leitungen benötigt: &#039;&#039;&#039;RTS&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;equest &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end) und &#039;&#039;&#039;CTS&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end). Jeder der beiden Kommunikationspartner ist verpflichtet, bevor ein Zeichen gesendet wird, den Zustand der &#039;&#039;&#039;RTS&#039;&#039;&#039; Leitung zu überprüfen. Nur wenn die Gegenstelle darauf Empfangsbereitschaft signalisiert, darf das Zeichen gesendet werden. Um der Gegenstelle zu signalisieren, daß sie zur Zeit keine Zeichen schicken soll, wird die Leitung &#039;&#039;&#039;CTS&#039;&#039;&#039; benutzt.&lt;br /&gt;
&lt;br /&gt;
===Softwarehandshake (XON/XOFF)===&lt;br /&gt;
Beim Softwarehandshake sind keine speziellen Leitungen notwendig. Statt dessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, daß Senden einzustellen bzw. wieder aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;XOFF&#039;&#039;&#039; Aufforderung das Senden einzustellen&lt;br /&gt;
* &#039;&#039;&#039;XON&#039;&#039;&#039;  Gegenstelle darf wieder senden&lt;br /&gt;
&lt;br /&gt;
Nachteilig bei einem Softwarehandshake ist es, daß dadurch keine direkte binäre Datenübertragung mehr möglich ist. Von den möglichen 256 Bytewerten werden ja 2 (nämlich &#039;&#039;&#039;XON&#039;&#039;&#039; und &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus.&lt;br /&gt;
&lt;br /&gt;
==Fehlersuche==&lt;br /&gt;
Erstaunlich oft wird im Forum der Hilferuf laut: &amp;quot;Meine UART funktioniert nicht, was mache ich falsch&amp;quot;. In der überwiegenden Mehrzahl der Fälle stellt sich dann heraus, daß es sich um ein Hardwareproblem handelt, wobei da wiederrum der Löwenanteil auf das Konto einer nicht korrekt eingestellten Taktrate geht: Der µC benutzt nicht einen angeschlossenen Quarz, so wie er auch im Programm eingetragen ist, sondern läuft immer noch mit dem internen RC-Takt. Daraus resultiert aber auch, daß der Baudraten Konfigurationswert falsch berechnet wird.&lt;br /&gt;
&lt;br /&gt;
Eine Checkliste zum Aufspüren solcher Fehler findet sich [[AVR_Checkliste#UART.2FUSART|hier]].&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Analoge Eingangswerte werden in der Regel über den AVR Analog-Digital-Converter (AD-Wandler, ADC) eingelesen, der in vielen Typen verfügbar ist (typisch 10bit Auflösung). Durch diesen werden analogen Signale (Spannungen) in digitale Zahlenwerte gewandelt. Bei AVRs, die über keinen internen AD-Wandler verfügen (z.B. ATmega162, ATtiny2313), kann durch externe Beschaltung (R/C-Netzwerk und &amp;quot;Zeitmessung&amp;quot;) die Funktion des AD-Wandlers &amp;quot;emuliert&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
Es existieren keine AVRs mit eingebautem Digital-Analog-Konverter (DAC). Diese Funktion muss durch externe Komponenten nachgebildet werden (z.B. PWM und &amp;quot;Glättung&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Unabhängig davon besteht natürlich immer die Möglichkeit, spezielle Bausteine zur Analog-Digital- bzw. Digital-Analog-Wandlung zu nutzen und diese über eine digitale Schnittstelle (z.b. SPI oder I2C) mit einem AVR anzusteuern.&lt;br /&gt;
&lt;br /&gt;
== AC (Analog Comparator) ==&lt;br /&gt;
&lt;br /&gt;
Der Comparator vergleicht 2 Spannungen an den Pins AIN0 und AIN1 und gibt einen Status aus welche der beiden Spannungen größer ist. AIN0 Dient dabei als Referenzspannung (Sollwert) und AIN1 als Vergleichsspannung (Istwert). Als Referenzspannung kann auch alternativ eine interne Referenzspannung ausgewählt werden.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Liegt die Vergleichsspannung (IST) unter der der Referenzspannung (SOLL) gibt der Comperator eine logische 0 aus. ISt die Vergleichsspannung hingegen größer als die Referenzspannung wird eine logische 1 ausgegeben.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Der Comperator arbeitet völlig autark bzw. parallel zum Prozessor. Für mobile Anwendungen empfiehlt es sich ihn abzuschalten sofern er nicht benötigt wird, da er ansonsten Strom benötigt. Der Comparator kann Interruptgesteuert abgefragt werden oder im Pollingbetrieb.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Steuer- bzw. Statusregister ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ACSR (0x28) - Analog Comparator Status Register&amp;lt;BR&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACD&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;AINBG&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACO&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACI&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | n/a&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 7 ACD - Analog Comperator Disable: 0 = Comparator ein, 1 = Comparator aus. Wird dieses Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 ACIE ggf. abgeschaltet werden.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 6 AINBG - Analog Comparator Bandgap Select: Ermöglicht das umschalten zwischen interner und externer Referenzspannung. 1 = interne (~1,3 Volt), 0 = externe Referenzspannung (an Pin AIN0)&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 5 ACO - Analog Coparator Output: Hier wird das Ergebnis des Vergleichs angezeigt. Es liegt typischerweise nach 1-2 Taktzyklen vor. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ergebnis: &amp;lt;BR&amp;gt;IST &amp;lt; SOLL --&amp;gt; 0&amp;lt;BR&amp;gt;&lt;br /&gt;
IST &amp;gt; SOLL --&amp;gt; 1&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4 ACI - Analog Comparator Interrupt Flag: Dieses Bit wird von der Hardware gesetzt wenn ein Interruptereignis das in Bit 0 und 1 definiert ist eintritt. Dieses Bit löst noch keinen Interrupt aus! Die Interruptroutine wird nur dann ausgeführt wenn das Bit 3 ACIE gesetzt ist und global Interrupts erlaubt sind (I-Bit in SREG=1). Das Bit 4 ACI wird wieder gelöscht wenn die Interruptroutine ausgeführt wurde oder wenn manuell das Bit auf 1 gesetzt wird. Das Bit kann für Abfragen genutzt werden, steuert oder konfuguriert aber nicht den Comparator.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 3 ACIE - Analog Comparator Interrupt Enable: Ist das Bit auf 1 gesetzt wird immer ein Interrupt ausgelöst wenn das Ereignis das in Bit 1 und 0 definiert ist eintritt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 2 ACIC - Analog Comparator Input Capture Enable: Wird das Bit gesetzt wird der Comparatorausgang intern mit dem Counter 1 verbunden. Es könnten damit z.b. die Anzahl der Vergleiche im Counter1 gezählt werden. Um den Comparator an den Timer1 Input Capture Interrupt zu verbinden muß im Timerregister das TICIE1 Bit auf 1 gesetzt werden. Der Trigger wird immer dann ausgelöst wenn das in Bit 1 und 0 definierte Ereignis eintritt.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 1,0 ACIS1,ACIS0 - Analog Comparator Interrupt select: Hier wird definiert welche Ereignisse einen Interrupt auslösen sollen:&amp;lt;BR&amp;gt;&lt;br /&gt;
00 = Interrupt auslösen bei jedem Flankenwechsel&amp;lt;BR&amp;gt;&lt;br /&gt;
10 = Interrupt auslösen bei fallender Flanke&amp;lt;BR&amp;gt;&lt;br /&gt;
11 = Interrupt auslösen bei steigender Flanke&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Werden diese Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 gelöscht werden.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der Analog-Digital-Konverter (ADC) wandelt analoge Signale in digitale Werte um, welche vom Controller interpretiert werden können. Einige AVR-Typen haben bereits einen mehrkanaligen Analog-Digital-Konverter eingebaut. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst werden kann, wird durch die Auflösung des ADC in Anzahl Bits angegeben, man hört bzw. liest jeweils von 8-Bit-ADC oder 10-Bit-ADC oder noch höher. ADCs die in AVRs enthalten sind haben zur Zeit eine maximale Auflösung von 10-Bit.&lt;br /&gt;
&lt;br /&gt;
Ein ADC mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des Analog-Digital-Konverters ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Der interne ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem Analog-Digital-Wandler (ADC) zurückgreifen, die über mehrere Kanäle verfügen. Kanäle heißt in diesem Zusammenhang, dass zwar bis zu zehn analoge Eingänge am AVR verfügbar sind, aber nur ein &amp;quot;echter&amp;quot; Analog-Digital-Wandler zur Verfügung steht, vor der eigentlichen Messung ist also einzustellen, welcher Kanal (&amp;quot;Pin&amp;quot;) mit dem Wandler verbunden und gemessen wird.&lt;br /&gt;
&lt;br /&gt;
Die Umwandlung innerhalb des AVR basiert auf der schrittweisen Näherung. Beim AVR müssen die Pins &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AVCC&#039;&#039;&#039; beschaltet werden. Für genaue Messungen sollte AVCC über ein L-C Netzwerk mit VCC verbunden werden, um Spannungsspitzen und -einbrüche vom Analog-Digital-Wandler fernzuhalten. Im Datenblatt findet sich dazu eine Schaltung, die 10uH und 100nF vorsieht.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Analog-Digital-Wandlung wird auf eine Referenzspannung bezogen. Aktuelle AVRs bieten drei Möglichkeiten zur Wahl dieser Spannung:&lt;br /&gt;
&lt;br /&gt;
* Eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; am Anschlusspin &#039;&#039;&#039;AREF&#039;&#039;&#039;. Die minimale (externe) Referenzspannung darf jedoch nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. &lt;br /&gt;
&lt;br /&gt;
* Verfügt der AVR über eine interne Referenzspannung, kann diese genutzt werden. Alle aktuellen AVRs mit internem AD-Wandler sollten damit ausgestattet sein (vgl. Datenblatt: 2,56V oder 1,1V je nach Typ). Das Datenblatt gibt auch über die Genauigkeit dieser Spannung Auskunft.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;interne&#039;&#039;&#039; Referenzspannung wird auf &#039;&#039;&#039;Vcc&#039;&#039;&#039; bezogen, eine externe Referenzspannung auf &#039;&#039;&#039;GND (Masse)&#039;&#039;&#039;. Davon unabhängig werden digitalisierte Spannungen &#039;&#039;&#039;immer&#039;&#039;&#039; auf GND bezogen. Beim ATMEGA8 z.B. ist der Pin AREF über 32kOhm mit GND verbunden, d.h. man muss diese doch extrem niedrige Eingangsimpedanz mit in die Berechnung für einen Spannungsteiler einbeziehen, bzw. kann diesen Widerstand als R2 gleich mit benutzen. Formel für Spannungsteiler: Udiv = U / ((R1 + R2) / R2)&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung von Vcc oder der internen Referenz wird empfohlen, einen Kondensator zwischen dem AREF-Pin und GND anzuordnen. Die Festlegung, welche Spannungsreferenz genutzt wird, erfolgt z.B. beim ATmega16 mit den Bits REFS1/REFS0 im ADMUX-Register. Die zu messende Spannung muss im Bereich zwischen &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AREF&#039;&#039;&#039; (egal ob intern oder extern) liegen. &lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; kann in zwei verschiedenen Betriebsarten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
; Einfache Wandlung (Single Conversion) : In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestoßen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
; Frei laufend (Free Running) : In dieser Betriebsart erfasst der Wandler permanent die anliegende Spannung und schreibt diese in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Die Register des ADC ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; verfügt über eigene Register. Im Folgenden die Registerbeschreibung eines  ATMega16, welcher über 8 ADC-Kanäle verfügt. Die Register unterscheiden sich jedoch nicht erheblich von denen anderer AVRs (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister A.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den &#039;&#039;&#039;ADC&#039;&#039;&#039; verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADEN&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able)&lt;br /&gt;
:Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren. Wenn das Bit nicht gesetzt ist, können die Pins wie normale I/O-Pins verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADSC&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion)&lt;br /&gt;
:Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.&lt;br /&gt;
:Wenn das Bit nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.&lt;br /&gt;
:Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADFR&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;F&#039;&#039;&#039;ree &#039;&#039;&#039;R&#039;&#039;&#039;un select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Ist das Bit auf 1 gesetzt arbeitet der ADC im Freerunning Modus. Dabe iwird das Datenregister permanent aktualisiert. Ist das Bit hingegen auf 0 gesetzt macht der ADC nur eine Single Conversion. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIF&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag)&lt;br /&gt;
:Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt, sobald eine Umwandlung erfolgt ist und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert wurde. Das Bit wird bei lesendem Zugriff auf &#039;&#039;&#039;ADC(L,H)&#039;&#039;&#039; automatisch (d.h. durch die Hardware) gelöscht.&lt;br /&gt;
:Wenn das &#039;&#039;&#039;ADIE&#039;&#039;&#039; Bit sowie das &#039;&#039;&#039;I-Bit&#039;&#039;&#039; im AVR &#039;&#039;&#039;Statusregister&#039;&#039;&#039; gesetzt ist, wird der &#039;&#039;&#039;ADC Interrupt&#039;&#039;&#039; ausgelöst und die Interrupt-Behandlungsroutine aufgerufen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn die Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 in das Register geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIE&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;, dann wird der &#039;&#039;&#039; ADC-Interrupt&#039;&#039;&#039; aktiviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADPS2...ADPS0&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;P&#039;&#039;&#039;rescaler &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
:Der &#039;&#039;&#039;ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz ergibt.&lt;br /&gt;
:Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
TF_{min}=\frac{CLK}{200\,\mathrm{kHz}}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50\,\mathrm{kHz}}=\frac{4000000}{50000}=\mathbf{80}&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im Interesse der schnelleren Wandlungszeit werden wir hier den Faktor 32 einstellen.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCL&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ADCH&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC &#039;&#039;&#039; Data Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn eine Umwandlung abgeschlossen ist, befindet sich der gemessene Wert in&lt;br /&gt;
diesen beiden Registern. Von &#039;&#039;&#039;ADCH&#039;&#039;&#039; werden nur die beiden niederwertigsten Bits verwendet. Es müssen immer beide Register ausgelesen werden, und zwar immer &#039;&#039;&#039;in der Reihenfolge: ADCL, ADCH&#039;&#039;&#039;. &lt;br /&gt;
Der effektive Messwert ergibt sich dann zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCL;       // mit uint16_t x&lt;br /&gt;
x += (ADCH&amp;lt;&amp;lt;8); // in zwei Zeilen (LSB/MSB-Reihenfolge und&lt;br /&gt;
                // C-Operatorpriorität sichergestellt)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADMUX&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Mu&#039;&#039;&#039;ltiple&#039;&#039;&#039;x&#039;&#039;&#039;er Select Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Register wird der zu messende Kanal ausgewählt. Beim 90S8535&lt;br /&gt;
kann jeder Pin von Port A als &#039;&#039;&#039;ADC&#039;&#039;&#039;-Eingang verwendet werden (=8 Kanäle).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADLAR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX4&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX3&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;REFS1...REFS0&#039;&#039;&#039; (&#039;&#039;&#039;Ref&#039;&#039;&#039;erence&#039;&#039;&#039;S&#039;&#039;&#039;election Bits)&lt;br /&gt;
:Mit diesen Bits kann die Referenzspannung eingestellt werden:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Referenzspanung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Externes AREF&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | AVCC als Referenz&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Interne 2,56 Volt&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADLAR&#039;&#039;&#039; (&#039;&#039;&#039;ADC &#039;&#039;&#039; &#039;&#039;&#039;L&#039;&#039;&#039;eft &#039;&#039;&#039;A&#039;&#039;&#039;djust &#039;&#039;&#039;R&#039;&#039;&#039;esult)&lt;br /&gt;
:Das ADLAR Bit verändert das Aussehen des Ergebnisses der AD-Wandlung. Bei einer logischen 1 wird das Ergebnis linksbündig ausgegeben, bei einer 0 rechtsündig. Eine Änderung in diesem Bit beeinflusst das Ergebnis sofort, ganz egal ob bereits eine Wandlung läuft.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX4...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 5 Bits wird der zu messende Kanal bestimmt. Wenn man einen einfachen 1-kanaligen ADC verwendet wird einfach die entsprechende Pinnummer des Ports in die Bits 0...2 eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
:Eine Empfehlung ist deswegen diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren des ADC ====&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039; ADC&#039;&#039;&#039; zu aktivieren, müssen wir das &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bit im &#039;&#039;&#039;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
Ein kleines Beispiel für den &amp;quot;single conversion&amp;quot;-Mode bei einem ATmega169 und Nutzung der internen Referenzspannung (beim &#039;169 1,1V bei anderen AVRs auch 2,56V). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler einstellen. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1023 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint16_t ReadChannel(uint8_t mux)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
&lt;br /&gt;
  ADMUX = mux;                      // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler &lt;br /&gt;
                               // setzen auf 8 (1) und ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  /* nach Aktivieren des ADC wird ein &amp;quot;Dummy-Readout&amp;quot; empfohlen, man liest&lt;br /&gt;
     also einen Wert und verwirft diesen, um den ADC &amp;quot;warmlaufen zu lassen&amp;quot; */&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
     ;     // auf Abschluss der Konvertierung warten &lt;br /&gt;
  }&lt;br /&gt;
  result = ADCW;  // ADCW muss einmal gelesen werden,&lt;br /&gt;
                  // sonst wird Ergebnis der nächsten Wandlung&lt;br /&gt;
                  // nicht übernommen.&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  result = 0; &lt;br /&gt;
  for( i=0; i&amp;lt;4; i++ )&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);            // eine Wandlung &amp;quot;single conversion&amp;quot;&lt;br /&gt;
    while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
      ;   // auf Abschluss der Konvertierung warten&lt;br /&gt;
    }&lt;br /&gt;
    result += ADCW;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Beispielaufrufe: */&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
&lt;br /&gt;
  adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -&amp;gt; Channel 0 */&lt;br /&gt;
  ...&lt;br /&gt;
  adcval = ReadChannel(2); /* MUX-Bits auf 0b0010 -&amp;gt; Channel 2 */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekennzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Das Löschen des ADIF-Flags sollte, &#039;&#039;&#039;entgegen&#039;&#039;&#039; der [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_intbits FAQ], mit&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADIF);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
erfolgen. Die Methode in der FAQ eignet sich nur für Register in denen &#039;&#039;&#039;nur&#039;&#039;&#039; Interrupt-Flags stehen.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Analog-Digital-Wandlung ohne internen ADC ===&lt;br /&gt;
&lt;br /&gt;
==== Messen eines Widerstandes ====&lt;br /&gt;
&lt;br /&gt;
Analoge Werte lassen sich ohne Analog-Digital-Wandler auch indirekt ermitteln. Im Folgenden wird die Messung des an einem Potentiometer eingestellten Widerstands anhand der Ladekurve eines Kondensators erläutert. Bei dieser Methode wird nur ein Portpin benötigt, ein Analog-Digital-Wandler oder Analog-Comparator ist nicht erforderlich. Es wird dazu ein Kondensator und der Widerstand (das Potentiometer) in Reihe zwischen Vorsorgungsspannung und Masse/GND geschaltet (sogen. RC-Netzwerk). Zusätzlich wird eine Verbindung der Leitung zwischen Kondensator und Potentiometer zu einem Portpin des Controllers hergestellt. Die folgende Abbildung verdeutlicht die erforderliche Schaltung. &lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wird der Portpin des Controllers auf Ausgang konfiguriert (im Beispiel &#039;&#039;DDRD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) und dieser Ausgang auf Logisch 1 (&amp;quot;High&amp;quot;, &#039;&#039;PORTD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) geschaltet, liegt an beiden &amp;quot;Platten&amp;quot; des Kondensators das gleiche Potential &#039;&#039;&#039;VCC&#039;&#039;&#039; an und der Kondensator somit entladen. (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so, da an beiden Seiten des Kondensators das gleiche Potential anliegt und somit eine Potentialdifferenz von 0V besteht =&amp;gt; Kondensator ist entladen).&lt;br /&gt;
&lt;br /&gt;
Nach einer gewissen Zeit ist der Kondensator entladen und der Portpin wird als Eingang konfiguriert (&#039;&#039;DDRD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2); PORTD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2)&#039;&#039;), wodurch dieser hochohmig wird. Der Status des Eingangspin (in PIND) ist Logisch 1 (High). Der Kondensator lädt sich jetzt über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), wird das Eingangssignal als LOW erkannt (Bit in PIND wird 0). Die Zeitspanne zwischen der Umschaltung von Entladung auf Aufladung und dem Wechsel des Eingangssignals von High auf Low ist ein Maß für den am Potentiometer eingestellten Widerstand. Zur Zeitmessung kann einer der im Controller vorhandenen Timer genutzt werden. Der 220 Ohm Widerstand dient dem Schutz des Controllers. Es würde sonst bei Maximaleinstellung des Potentionmeters (hier 0 Ohm) ein zu hoher Strom fließen, der die Ausgangsstufe des Controllers zerstört. &lt;br /&gt;
&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B: 0...100 % oder so) umzurechnen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Link 404 =&amp;gt; auskommentiert, mthomas 9.2.2008 &lt;br /&gt;
Ein Beispielprogramm findet sich auf [http://www.mypage.bluewin.ch/ch_schifferle/ Christian Schifferles Web-Seite] im Archiv &#039;&#039;ATMEL.ZIP&#039;&#039;, welches unter den Titel &#039;&#039;Tutorial &amp;quot;Programmieren mit C für Atmel Mikrocontroller&#039;&#039; heruntergeladen werden kann. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ADC über Komparator ====&lt;br /&gt;
&lt;br /&gt;
Es gibt einen weiteren Weg, eine analoge Spannung mit Hilfe des&lt;br /&gt;
Komparators, welcher in fast jedem AVR integriert ist, zu messen. Siehe dazu&lt;br /&gt;
auch die Application Note AVR400 von Atmel.&lt;br /&gt;
&lt;br /&gt;
Dabei wird das zu messende Signal auf den invertierenden Eingang&lt;br /&gt;
des Komparators geführt. Zusätzlich wird ein Referenzsignal an den nicht&lt;br /&gt;
invertierenden Eingang des Komparators angeschlossen. Das Referenzsignal wird&lt;br /&gt;
hier auch wieder über ein RC-Glied erzeugt, allerdings mit festen Werten für R&lt;br /&gt;
und C.&lt;br /&gt;
&lt;br /&gt;
[[Image:ADC ueber Komparator.gif]]&lt;br /&gt;
&lt;br /&gt;
Das Prinzip der Messung ist nun dem vorhergehenden recht&lt;br /&gt;
ähnlich. Durch Anlegen eines LOW-Pegels an Pin 2 wird der Kondensator zuerst&lt;br /&gt;
einmal entladen. Auch hier muss darauf geachtet werden, dass der Entladevorgang&lt;br /&gt;
genügend lang dauert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Nun wird Pin 2 auf HIGH gelegt. Der Kondensator wird geladen. Wenn die Spannung&lt;br /&gt;
über dem Kondensator die am Eingangspin anliegende Spannung erreicht hat&lt;br /&gt;
schaltet der Komparator durch. Die Zeit, welche benötigt wird, um den&lt;br /&gt;
Kondensator zu laden kann nun auch wieder als Maß für die Spannung an Pin 1&lt;br /&gt;
herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Ich habe es mir gespart, diese Schaltung auch aufzubauen und&lt;br /&gt;
zwar aus mehreren Gründen:&lt;br /&gt;
&lt;br /&gt;
# 3 Pins notwendig.&lt;br /&gt;
# Genauigkeit vergleichbar mit einfacherer Lösung.&lt;br /&gt;
# War einfach zu faul.&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung liegt allerdings darin, dass damit&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines Digital-Analog-Konverters (&#039;&#039;&#039;DAC&#039;&#039;&#039;) können wir nun auch Analogsignale ausgeben. Es gibt hier mehrere Verfahren. &amp;lt;!-- Wenn wir beim ADC die Möglichkeit haben, mit externen Komponenten zu operieren, müssen wir bei der DAC-Wandlung mit dem auskommen, was der Controller selber zu bieten hat. --mt: hmm, richtig? verstaendlich? redundant? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DAC über mehrere digitale Ausgänge ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir an den Ausgängen des Controllers ein entsprechendes&lt;br /&gt;
Widerstandsnetzwerk aufbauen haben wir die Möglichkeit, durch die Ansteuerung&lt;br /&gt;
der Ausgänge über den Widerständen einen Addierer aufzubauen, mit dessen&lt;br /&gt;
Hilfe wir eine dem Zahlenwert proportionale Spannung erzeugen können. Das&lt;br /&gt;
Schaltbild dazu kann etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
[[Image:DAC R2R.gif]]&lt;br /&gt;
&lt;br /&gt;
Es sollten selbstverständlich möglichst genaue Widerstände verwendet&lt;br /&gt;
werden, also nicht unbedingt solche mit einer Toleranz von 10% oder mehr.&lt;br /&gt;
Weiterhin empfiehlt es sich, je nach Anwendung den Ausgangsstrom über einen&lt;br /&gt;
Operationsverstärker zu verstärken.&lt;br /&gt;
&lt;br /&gt;
=== PWM (Pulsweitenmodulation) ===&lt;br /&gt;
&lt;br /&gt;
Wir kommen nun zu einem Thema, welches in aller Munde ist, aber viele&lt;br /&gt;
Anwender verstehen nicht ganz, wie &#039;&#039;&#039; PWM&#039;&#039;&#039; eigentlich funktioniert.&lt;br /&gt;
&lt;br /&gt;
Wie wir alle wissen, ist ein Mikrocontroller ein rein digitales Bauteil.&lt;br /&gt;
Definieren wir einen Pin als Ausgang, dann können wir diesen Ausgang entweder&lt;br /&gt;
auf HIGH setzen, worauf am Ausgang die Versorgungsspannung &#039;&#039;&#039; Vcc&#039;&#039;&#039; anliegt, oder aber wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt. Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen HIGH und LOW umschalten? - Richtig, wir erhalten eine Rechteckspannung, wie die folgende Abbildung zeigt:&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 1.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Rechteckspannung hat nun einen arithmetischen Mittelwert, &lt;br /&gt;
der je nach Pulsbreite kleiner oder größer ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 2.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun diese pulsierende Ausgangsspannung noch über ein RC-Glied filtern/&amp;quot;glätten&amp;quot;, dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVRs können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. &lt;br /&gt;
Dazu dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt, weshalb ein Blick ins entsprechende Datenblatt dringend angeraten wird.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren, müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| PWM-Modus des Timers ist nicht aktiv.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Timer/Counter zählt nun permanent von 0 bis zur Obergrenze&lt;br /&gt;
und wieder zurück, er wird also als sogenannter Auf-/Ab Zähler betrieben. &lt;br /&gt;
Die Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss mit den Bits &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; desselben&lt;br /&gt;
Registers die gewünschte Ausgabeart des Signals definiert werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Nicht invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Hochzählen und gesetzt beim&lt;br /&gt;
Herunterzählen.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Herunterzählen und gesetzt beim&lt;br /&gt;
Hochzählen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl, um beispielsweise den Timer/Counter als&lt;br /&gt;
nicht invertierenden 10-Bit PWM zu verwenden, heißt dann:&lt;br /&gt;
&lt;br /&gt;
alte Schreibweise (PWMxx wird nicht mehr akzeptiert)&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;PWM11)|(1&amp;lt;&amp;lt;PWM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
neue Schreibweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;WGM11)|(1&amp;lt;&amp;lt;WGM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Timer/Counter überhaupt läuft, müssen wir im Control&lt;br /&gt;
Register B &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; noch den gewünschten Takt (Vorteiler) einstellen und&lt;br /&gt;
somit auch die Frequenz des &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signals bestimmen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stop. Der Timer/Counter wird gestoppt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin 1, negative Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin 1, positive Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Also um einen Takt von CK / 1024 zu generieren, verwenden wir&lt;br /&gt;
folgenden Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1B = (1&amp;lt;&amp;lt;CS12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt muss nur noch der Vergleichswert festgelegt werden. Diesen&lt;br /&gt;
schreiben wir in das 16-Bit Timer/Counter Output Compare Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem Vergleichswert und dem generierten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal aufzeigen.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
Ach ja, fast hätte ich&#039;s vergessen. Das generierte &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal&lt;br /&gt;
wird am Output Compare Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; des Timers ausgegeben und leider können wir&lt;br /&gt;
deshalb auch beim AT90S2313 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren. Andere AVR-Typen verfügen über bis zu vier PWM-Ausgänge. Zu beachten ist außerdem, das wenn der OC Pin aktiviert ist, er nichtmehr wie üblich funktioniert und z.B. nicht einfach über PINx ausgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Ein Programm, dass an einem ATMega8 den Fast-PWM Modus verwendet, den Modus 14, könnte so aussehen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // OC1A auf Ausgang&lt;br /&gt;
  DDRB = (1 &amp;lt;&amp;lt; PB1 );&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // Timer 1 einstellen&lt;br /&gt;
  //  &lt;br /&gt;
  // Modus 14:&lt;br /&gt;
  //    Fast PWM, Top von ICR1&lt;br /&gt;
  //&lt;br /&gt;
  //     WGM13    WGM12   WGM11    WGM10&lt;br /&gt;
  //      1        1       1        0&lt;br /&gt;
  //&lt;br /&gt;
  //    Timer Vorteiler: 1&lt;br /&gt;
  //     CS12     CS11    CS10&lt;br /&gt;
  //       0        0       1&lt;br /&gt;
  //&lt;br /&gt;
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match&lt;br /&gt;
  //     COM1A1   COM1A0&lt;br /&gt;
  //       1        0&lt;br /&gt;
 &lt;br /&gt;
  TCCR1A = (1&amp;lt;&amp;lt;COM1A1) | (1&amp;lt;&amp;lt;WGM11);&lt;br /&gt;
  TCCR1B = (1&amp;lt;&amp;lt;WGM13) | (1&amp;lt;&amp;lt;WGM12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  //  den Endwert (TOP) für den Zähler setzen&lt;br /&gt;
  //  der Zähler zählt bis zu diesem Wert&lt;br /&gt;
&lt;br /&gt;
  ICR1 = 0x6FFF;&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  // der Compare Wert&lt;br /&gt;
  // Wenn der Zähler diesen Wert erreicht, wird mit&lt;br /&gt;
  // obiger Konfiguration der OC1A Ausgang abgeschaltet&lt;br /&gt;
  // Sobald der Zähler wieder bei 0 startet, wird der&lt;br /&gt;
  // Ausgang wieder auf 1 gesetzt&lt;br /&gt;
  //&lt;br /&gt;
  // Durch Verändern dieses Wertes, werden die unterschiedlichen&lt;br /&gt;
  // PWM Werte eingestellt.&lt;br /&gt;
&lt;br /&gt;
  OCR1A = 0x3FFF;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  while( 1 )&lt;br /&gt;
    ;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für Details der PWM Möglichkeiten, muß immer das jeweilge Datenblatt des Prozessors konsultiert werden, da sich die unterschiedlichen Prozessoren in ihren Möglichkeiten doch stark unterscheiden. Auch muß man aufpassen, welches zu setzende Bit in welchem Register sind. Auch hier kann es sein, dass gleichnamige Konfigurationsbits in unterschiedlichen Konfigurationsregistern (je nach konkretem Prozessortyp) sitzen.&lt;br /&gt;
&lt;br /&gt;
= LCD-Ansteuerung =&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung an der LCD-Controller-Platine ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; (ist in manchen Unterlagen auch &#039;&#039;&#039;EN&#039;&#039;&#039;  für &#039;&#039;Enable&#039;&#039; abgekürzt) benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert, ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitetet hat (diese Methode u.a. in der LCD-Library von Peter Fleury verwendet). Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt da wir wissen, welche Anschlüsse das LCDs benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin #-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin-µC&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND oder Poti (siehe oben)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD4 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD5 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD0 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD1 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD2 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD3 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man die Steuerleitungen EN und RS auf Pins an einem anderen Port legen möchte, kann man so wie in diesem [http://www.mikrocontroller.net/topic/88543#751982 Forumsbeitrag] vorgehen.&lt;br /&gt;
&lt;br /&gt;
Ok, alles ist verbunden, wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display? &lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
void lcd_data(unsigned char temp1);&lt;br /&gt;
void lcd_string(char *data);&lt;br /&gt;
void lcd_command(unsigned char temp1);&lt;br /&gt;
void lcd_enable(void);&lt;br /&gt;
void lcd_init(void);&lt;br /&gt;
void lcd_home(void);&lt;br /&gt;
void lcd_clear(void);&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y);&lt;br /&gt;
&lt;br /&gt;
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
&lt;br /&gt;
// LCD Befehle&lt;br /&gt;
&lt;br /&gt;
#define CLEAR_DISPLAY 0x01&lt;br /&gt;
#define CURSOR_HOME   0x02&lt;br /&gt;
&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
&lt;br /&gt;
#define LCD_PORT      PORTD&lt;br /&gt;
#define LCD_DDR       DDRD&lt;br /&gt;
#define LCD_RS        PD4&lt;br /&gt;
#define LCD_EN        PD5&lt;br /&gt;
// DB4 bis DB7 des LCD sind mit PD0 bis PD3 des AVR verbunden&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// sendet ein Datenbyte an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_data(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_RS);        // RS auf 1 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_command(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);        // RS auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;              // oberes Nibble holen&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;            // maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;            // unteres Nibble holen und maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// erzeugt den Enable-Puls&lt;br /&gt;
void lcd_enable(void)&lt;br /&gt;
{&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
    _delay_us(1);                   // kurze Pause&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/80900&lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Initialisierung: &lt;br /&gt;
// Muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
void lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
   LCD_DDR = LCD_DDR | 0x0F | (1&amp;lt;&amp;lt;LCD_RS) | (1&amp;lt;&amp;lt;LCD_EN);   // Port auf Ausgang schalten&lt;br /&gt;
&lt;br /&gt;
   // muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(15);&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x03;            &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);      // RS auf 0&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4 Bit Modus aktivieren &lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x02;&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4Bit / 2 Zeilen / 5x7&lt;br /&gt;
   lcd_command(0x28);&lt;br /&gt;
    &lt;br /&gt;
   // Display ein / Cursor aus / kein Blinken&lt;br /&gt;
   lcd_command(0x0C); &lt;br /&gt;
 &lt;br /&gt;
   // inkrement / kein Scrollen&lt;br /&gt;
   lcd_command(0x06);&lt;br /&gt;
&lt;br /&gt;
   lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
&lt;br /&gt;
void lcd_clear(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CLEAR_DISPLAY);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
&lt;br /&gt;
void lcd_home(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CURSOR_HOME);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)&lt;br /&gt;
&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t tmp;&lt;br /&gt;
&lt;br /&gt;
  switch (y) {&lt;br /&gt;
    case 1: tmp=0x80+0x00+x; break;    // 1. Zeile&lt;br /&gt;
    case 2: tmp=0x80+0x40+x; break;    // 2. Zeile&lt;br /&gt;
    case 3: tmp=0x80+0x10+x; break;    // 3. Zeile&lt;br /&gt;
    case 4: tmp=0x80+0x50+x; break;    // 4. Zeile&lt;br /&gt;
  }&lt;br /&gt;
  lcd_command(tmp);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_string(char *data)&lt;br /&gt;
{&lt;br /&gt;
    while(*data) {&lt;br /&gt;
        lcd_data(*data);&lt;br /&gt;
        data++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches die Funktionen benutzt, sieht zb. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen&lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    lcd_data(&#039;T&#039;);&lt;br /&gt;
    lcd_data(&#039;e&#039;);&lt;br /&gt;
    lcd_data(&#039;s&#039;);&lt;br /&gt;
    lcd_data(&#039;t&#039;);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
&lt;br /&gt;
    lcd_string(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist dabei, dass die Optimierung bei der Compilierung eingeschaltet ist, sonst stimmen die Zeiten der Funktionen _delay_us() und _delay_ms() nicht und der Code wird wesentlich länger (Siehe Dokumentation der libc im WinAVR).&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches eine Variable ausgibt, sieht zb. so aus.&lt;br /&gt;
Mittels der itoa() Funktion (itoa = &amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;nteger &amp;lt;b&amp;gt;To&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;A&amp;lt;/b&amp;gt;scii ) wird von einem Zahlenwert eine textuelle Repräsentierung ermittelt (sprich: ein String erzeugt) und dieser String mit der bereits vorhandenen Funktion lcd_string ausgegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen &lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Beispiel&lt;br /&gt;
int variable = 42;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe des Zeichens dessen ASCII-Code gleich dem Variablenwert ist&lt;br /&gt;
    // (Im Beispiel entspricht der ASCII-Code 42 dem Zeichen *)&lt;br /&gt;
    // http://www.code-knacker.de/ascii.htm&lt;br /&gt;
    lcd_data(variable);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
 &lt;br /&gt;
    // Ausgabe der Variable als Text in dezimaler Schreibweise&lt;br /&gt;
    {&lt;br /&gt;
       // ... umwandeln siehe FAQ Artikel bei www.mikrocontroller.net&lt;br /&gt;
       // WinAVR hat eine itoa()-Funktion, das erfordert obiges #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
       char Buffer[20]; // in diesem {} lokal&lt;br /&gt;
       itoa( variable, Buffer, 10 ); &lt;br /&gt;
&lt;br /&gt;
       // ... ausgeben  &lt;br /&gt;
       lcd_string( Buffer );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einrichten eines Projekts muss man zu der Datei mit dem Hauptprogramm auch die Datei lcd-routines.c in das Projekt aufnehmen. Dies geschieht beim AVR Studio unter Source Files im Fenster AVR GCC oder bei WinAVR im Makefile (z.B. durch SRC += lcd-routines.c).&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVRs sind für viele Steuerungsaufgaben zu schnell. Wenn wir beispielsweise eine LED oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die CPU-Frequenz verwenden, da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, Vorgänge in Zeitabständen durchzuführen, die geringer als die Taktfrequenz des Controllers sind. Selbstverständlich sollte die resultierende Frequenz auch noch möglichst genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist ganz einfach ein bestimmtes Register im µC, das völlig ohne Zutun des Programms, also per Hardware, hochgezählt wird. Das alleine wäre noch nicht allzu nützlich, wenn nicht dieses Hardwareregister bei bestimmten Zählerständen einen Interrupt auslösen könnte. Ein solches Ereignis ist der Overflow: Da die Bitbreite des Registers beschränkt ist, kommt es natürlich auch vor, dass der Zähler so hoch zählt, dass der nächste Zählerstand mit dieser Bitbreite nicht mehr darstellbar ist und der Zähler wieder auf 0 zurückgesetzt wird. Dieses Ereignis nennt man den Overflow und es ist möglich an dieses Ereignis einen Interrupt zu koppeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Alternativvorschlag mthomas &lt;br /&gt;
Jeder Timer verfügt über ein Zählerregister im Mikrocontroller, das automatisch und ohne Zutun des Programms von der Hardware weitergezählt wird. In einem einfachen Anwendungsfall stellt man den Timer auf eine Zählgeschwindigkeit (Frequenz) und kann dann anhand des Zählerstands ermitteln, wie viel Zeit vergangen ist. Das eigentlich Nützliche an Timern ist jedoch, dass man  bestimmte Zählerstände mit Interrupts verknüpfen kann, so dass der Controller beim Auftreten automatisch eine vom Anwender geschriebene Routine aufruft. Eines dieser möglichen Ereignis ist der Overflow ((Zähler-)Überlauf), der dann auftritt, wenn der Wert des Zählerregisters (Timer/Counter-Register) den maximal möglichen Wert überschreitet. Der Maximalwert wird durch die Bitbreite des Zählerregisters bestimmt (z.B. 255 bei 8-Bit Timern). Beim Überlauf/Overflow wird der Zähler durch die Hardware auf 0 zurückgesetzt und die Zählung beginnt von neuem. Wurde vorher der Overflow-Interrupt für den Timer aktiviert (im Timer Control Register) unterbricht der Controller automatisch die Ausführung des Hauptprogramms und verzweigt in die Interrupt-Routine des Anwenders.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auf den AT90S2313. Für andere Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer Auflösung von 65536. Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz, der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
== Der Vorteiler (Prescaler) ==&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler dient dazu, den CPU-Takt vorerst um einen einstellbaren Faktor zu reduzieren. Die so geteilte Frequenz wird den Eingängen der Timer zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024 einstellen, wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca. 4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw. Zählregister (TCNTx) mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle verfügen über mindestens einen, teilweise sogar zwei, 8-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird z.B bei AT90S2313 über folgende Register angesprochen (bei anderen Typen weitestgehend analog):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    TCCR0 |= (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst und die daran gebundene Interrupt-Routine abgearbeitet. Das TOV Flag &lt;br /&gt;
lässt sich durch das Hineinschreiben einer 1 und nicht wie erwartet einer 0 wieder zurücksetzen.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen außer den 8-Bit Timern auch 16-Bit Timer. Die 16-Bit Timer/Counter sind etwas komplexer aufgebaut als die 8-Bit Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM13&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM12 (CTC1)&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorteiler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt, und C der voreingestellte Vergleichswert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin T1, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin T1, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T1 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T1 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0, bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;TCNT1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;OCR1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;OCR1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;OCR1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;OCR1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäß folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]] [[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Ausserdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr. Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen kleiner 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4us warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.6 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t x;&lt;br /&gt;
&lt;br /&gt;
    x = 10;&lt;br /&gt;
&lt;br /&gt;
    while (x &amp;gt;= 0)&lt;br /&gt;
    {&lt;br /&gt;
      // tu was&lt;br /&gt;
&lt;br /&gt;
      x--;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog? ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Einige Controller haben einen eigenen Watchdog Oszillator, z.B. der Tiny2313 mit 128kHz. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde, beginnt der Counter von 0 an hochzuzählen. &lt;br /&gt;
Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern, müssen wir den Watchdog regelmäßig wieder neu starten bzw. rücksetzen (Watchdog Reset). &lt;br /&gt;
Dies sollte innerhalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern, muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten. Es müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. &lt;br /&gt;
Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist, wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei &#039;&#039;wdt.h&#039;&#039; (&#039;&#039;#include &amp;lt;avr/wdt.h&amp;gt;&#039;&#039;) in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch gestartet wird. &lt;br /&gt;
Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. &lt;br /&gt;
Falls ein anderer Wert gewünscht ist, so kann dies im Makfile in den Linker-Optionen eingetragen werden. &lt;br /&gt;
Dazu muss in der Zeile LDFLAGS folgende Option angefügt werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert&lt;br /&gt;
:Mögliche Timeoutwerte:&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Konstante&lt;br /&gt;
! Wert&lt;br /&gt;
! TimeOut&lt;br /&gt;
|- &lt;br /&gt;
| WDTO_15MS   &lt;br /&gt;
| 0&lt;br /&gt;
| 15 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_30MS   &lt;br /&gt;
| 1&lt;br /&gt;
| 30 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_60MS   &lt;br /&gt;
| 2&lt;br /&gt;
| 60 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_120MS   &lt;br /&gt;
| 3&lt;br /&gt;
| 120 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_250MS   &lt;br /&gt;
| 4&lt;br /&gt;
| 250 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_500MS   &lt;br /&gt;
| 5&lt;br /&gt;
| 500 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_1S   &lt;br /&gt;
| 6&lt;br /&gt;
| 1 S&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_2S   &lt;br /&gt;
| 7&lt;br /&gt;
| 2s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
== Watchdog-Anwendungshinweise ==&lt;br /&gt;
&lt;br /&gt;
Ob nun der Watchdog als Schutzfunktion überhaupt verwendet werden soll, hängt stark von der Anwendung, der genutzten Peripherie und dem Umfang und der Qualitätssicherung des Codes ab. Will man sicher gehen, dass ein Programm sich nicht in einer Endlosschleife verfängt, ist der Wachdog das geeignete Mittel dies zu verhindern. Weiterhin kann bei geschickter Programmierung der Watchdog dazu genutzt werden, bestimmte Stromsparfunktionen zu implementieren. Bei einigen neueren AVRs (z.B. dem ATTiny13) kann der Watchdog auch direkt als Timer genutzt werden, der den Controller aus einem Schlafmodus aufweckt. Auch dies kann im &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register eingestellt werden. Außerdem bietet der WD die einzige Möglichkeit einen beabsichtigten System-Reset (ein &amp;quot;richtiger Reset&amp;quot;, kein &amp;quot;jmp 0x0000&amp;quot;) ohne externe Beschaltung auszulösen, was z.B. bei der Implementierung eines Bootloaders nützlich ist. Bei bestimmten Anwendungen kann die Nutzung des WD als &amp;quot;ultimative Deadlock-Sicherung für nicht bedachte Zustände&amp;quot; natürlich immer als zusätzliche Sicherung dienen. &lt;br /&gt;
&lt;br /&gt;
Es besteht die Möglichkeit herauszufinden, ob ein Reset durch den Watchdog ausgelöst wurde (beim ATmega16 z.B. Bit WDRF in MCUCSR). Diese Information sollte auch genutzt werden, falls ein WD-Reset in der Anwendung nicht planmäßig implementiert wurde. Zum Beispiel kann man eine LED an einen freien Pin hängen, die nur bei einem Reset durch den WD aufleuchtet oder aber das &amp;quot;Ereignis WD-Reset&amp;quot; im internen EEPROM des AVR absichern, um die Information später z.B. über UART oder ein Display auszugeben (oder einfach den EEPROM-Inhalt über die ISP/JTAG-Schnittstelle auslesen).&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/77273#642501 Bug in ATtiny2313?]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt der Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft. Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe Abschnitt (TODO: verlinken) im Anhang.&lt;br /&gt;
&amp;lt;!--Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. vgl. [[AVR-GCC-Tutorial#Anhang|Anhang]])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.B. &#039;&#039;void TIMER0_OVF_vect(void)...&#039;&#039;). Der Unterschied im Vergleich zu ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben.  &amp;lt;!--Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)), als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur &lt;br /&gt;
in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechnungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechnungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0, PORTA ist danach 0b00000001. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-oder-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8 (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten &#039;&#039;Sleep-Modes&#039;&#039; (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_enable()&#039;&#039;&#039;&lt;br /&gt;
:aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_cpu()&#039;&#039;&#039;&lt;br /&gt;
: Versetzt den Controller in den Schlafmodus (sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt)&lt;br /&gt;
* &#039;&#039;&#039;sleep_disable()&#039;&#039;&#039;&lt;br /&gt;
:deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts.&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;allozieren&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich, um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory, bei AVR Controllern mit mehr als 64kB Flash auch elpm). Die Standard-Laufzeitbibliothek des avr-gcc (die avr-libc) stellt diese Funktionen nach Einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Lokale Variablen (eigentlich Konstanten) innerhalb von Funktionen können ebenfalls im Programmspeicher abgelegt werden. Dazu ist bei der Definition jedoch ein &#039;&#039;static&#039;&#039; voranzustellen, da solche &amp;quot;Variablen&amp;quot; nicht auf dem Stack bzw. (bei Optimierung) in Registern verwaltet werden können. Der Compiler &amp;quot;wirft&amp;quot; eine Warnung falls static fehlt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray2 };&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  static /*const*/ uint8_t pgmTestByteLocal PROGMEM = 0x55;&lt;br /&gt;
  static /*const*/ char pgmTestStringLocal[] PROGMEM = &amp;quot;im Flash&amp;quot;;&lt;br /&gt;
  // so nicht (static fehlt): char pgmTestStringLocalFalsch [] PROGMEM = &amp;quot;so nicht&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 // ...&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
    //...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte....&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWort);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;groß&amp;quot; (Stand avr-gcc 3.4.x). Damit ist der mögliche Speicherbereich für &amp;quot;Flash-Konstanten&amp;quot; auf 64kB begrenzt.  &amp;lt;!-- Einige avr-libc/pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher) (intern via Assembler Anweisung ELPM). Die Initialisierungswerde des Speicherinhalts jenseits der 64kB-Marke müssen dann jedoch auf anderem Weg angelegt werden (nicht PROGMEM, evtl. eigene Section und Linker-Optionen - TODO) /// alt - und nicht ganz korrekt: (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.)--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray enthält nun die Startadresse des Byte-Arrays pgmFooByteArray1&lt;br /&gt;
    // Allerdings würde ein direkter Zugriff mit diesem Pointer (z.B. temp=*ptrToArray)&lt;br /&gt;
    // &#039;&#039;&#039;nicht&#039;&#039;&#039; den Inhalt von pgmFooByteArray1[0] liefern, sondern von einer Speicherstelle&lt;br /&gt;
    // im &#039;&#039;&#039;RAM&#039;&#039;&#039;, die die gleiche Adresse hat wie pgmFooByteArray1[0]&lt;br /&gt;
    // Daher muss nun die Funktion pgm_read_byte() benutzt werden, die die in ptrToArray&lt;br /&gt;
    // enthaltene Adresse benutzt und auf das Flash zugreift.&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray enthält nun die Adresse des ersten Elements des Byte-Arrays pgmFooByteArray2&lt;br /&gt;
    // da im zweiten Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmFooByteArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Array aus Zeichenketten im Flash-Speicher ===&lt;br /&gt;
&lt;br /&gt;
Felder aus Zeichenketten im Flash-Speicher werden in zwei Schritten angelegt: Zuerst die einzelnen Elemente des Arrays und im Anschluss ein Array, in dem die Addressen der Zeichenketten abgelegt werden. Zum Auslesen wird zuerst die Adresse des i-ten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, auf das Element (die Zeichenkette) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const char str1[] PROGMEM = &amp;quot;first_A&amp;quot;;&lt;br /&gt;
const char str2[] PROGMEM = &amp;quot;second_A&amp;quot;;&lt;br /&gt;
const char str3[] PROGMEM = &amp;quot;third_A&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char *strarray1[] PROGMEM = {&lt;br /&gt;
	str1,&lt;br /&gt;
	str2,&lt;br /&gt;
	str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	int i, j, l;&lt;br /&gt;
	const char *pstrflash;&lt;br /&gt;
	char work[20], work2[20];&lt;br /&gt;
	// fuer Simulation: per volatile Optimierung verhindern, &lt;br /&gt;
	//                  da c nicht genutzt&lt;br /&gt;
	volatile char c;&lt;br /&gt;
	&lt;br /&gt;
	for ( i = 0; i &amp;lt; (sizeof(strarray1)/sizeof(strarray1[0]) ); i++ ) {&lt;br /&gt;
&lt;br /&gt;
		// setze Pointer auf die Addresse des i-ten Elements des&lt;br /&gt;
		// &amp;quot;Flash-Arrays&amp;quot; (str1, str2, ...)&lt;br /&gt;
		pstrflash = (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) );&lt;br /&gt;
		&lt;br /&gt;
		// kopiere den Inhalt der Zeichenkette von der&lt;br /&gt;
		// in pstrflash abgelegten Adresse in das work-Array&lt;br /&gt;
		// analog zu strcpy( work, strarray1[i]) wenn alles im RAM&lt;br /&gt;
		strcpy_P( work, pstrflash );&lt;br /&gt;
		// verkuerzt:&lt;br /&gt;
		strcpy_P( work2, (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) ) );&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
		// Zeichen-fuer-Zeichen&lt;br /&gt;
		l = strlen_P( pstrflash );&lt;br /&gt;
		for ( j=0; j &amp;lt; l; j++ ) {&lt;br /&gt;
			// analog zu c=strarray[i][j] wenn alles im RAM&lt;br /&gt;
			c = (char)( pgm_read_byte( pstrflash++ ) );&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	while (1) { ; }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: &amp;quot;How do I put an array of strings completely in ROM?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5)) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Vorsicht: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char textImFlashOK[] PROGMEM = &amp;quot;mit[]&amp;quot;; &lt;br /&gt;
// = Daten im &amp;quot;Flash&amp;quot;, textImFlashOK* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char* textImFlashProblem PROGMEM = &amp;quot;mit*&amp;quot;;&lt;br /&gt;
// Konflikt: Daten im BSS (lies: RAM), textImFlashFAIL* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der Initialisierungsstring von &amp;quot;textImFlashProblem&amp;quot; zu den Konstanten ans Ende des Programmcodes gelegt wird (BSS), von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte (und wird). Da der lesende Code (mittels pgm_read*) trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein weiters Problem des AVR-GCC (gesehen bei avr-gcc 3.4.1 und 3.4.2) bei der Anpassung an die Harvard-Architektur zu sein (konstanter Pointer auf variable Daten?!). Abhilfe (&amp;quot;Workaround&amp;quot;): Initialisierung bei Zeichenketten mit [] oder gleich im Code PSTR(&amp;quot;...&amp;quot;) nutzen.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden, ob es sich um eine Adresse im Flash oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind.&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die einen im Flash abgelegten String z.B. an eine UART ausgibt, würde dann so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void uart_puts_p(const char *text)&lt;br /&gt;
{&lt;br /&gt;
    char Zeichen;&lt;br /&gt;
&lt;br /&gt;
    while (Zeichen = pgm_read_byte(text))&lt;br /&gt;
    {   /* so lange, wie mittels pgm_read_byte ein Zeichen vom Flash gelesen&lt;br /&gt;
           werden konnte, welches nicht das &amp;quot;String-Endezeichen&amp;quot; darstellt */&lt;br /&gt;
&lt;br /&gt;
        /* Das gelesene Zeichen über die normalen Kanäle verschicken */&lt;br /&gt;
        uart_putc(Zeichen);&lt;br /&gt;
        text++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Flash in der Anwendung schreiben ===&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option (auch bekannt als Bootloader-Support) können Teile des Flash-Speichers auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. Bei wenigen &amp;quot;kleinen&amp;quot; AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm(Flash)- und Datenspeicher(RAM) auf. Der C-Standard und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiß&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, dass vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgegeben sind, die innerhalb weniger Taktzyklen aufeinanderfolgen müssen (&amp;quot;timed sequence&amp;quot;). Auch dies muss bei Nutzung der Funktionen aus der avr-libc/eeprom.h-Datei nicht selbst implementiert werden. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Siehe dazu folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt; // wird in aktuellen Versionen der avr-lib mit xx.h eingebunden&lt;br /&gt;
&lt;br /&gt;
// EEMEM wird bei aktuellen Versionen der avr-lib in eeprom.h definiert&lt;br /&gt;
// hier: definiere falls noch nicht bekannt (&amp;quot;alte&amp;quot; avr-libc)&lt;br /&gt;
#ifndef EEMEM&lt;br /&gt;
// alle Textstellen EEMEM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEMEM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
//...&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // &#039;Variablen&#039; eeFooByte geschrieben&lt;br /&gt;
//...&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z.B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    #define EEPROM_DEF 0xFF&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ( ( myByte = eeprom_read_byte(&amp;amp;eeFooByte) ) == EEPROM_DEF ) {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
//...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprüft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und möglicherweise noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,3);&lt;br /&gt;
&lt;br /&gt;
    /* dito etwas anschaulicher aber &amp;quot;unnütze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fließkommazahlen lassen sich recht praktisch über eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t i[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    uint8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEMEM;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
//...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklaration abgeleiteten EEPROM-Inhalt in eine Datei geschrieben werden (übliche Dateiendung: .eep, Daten im Intel Hex-Format). Damit können recht elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen (siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles). Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden (Write EEPROM), wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse (vgl. Datenblatt Abschnitt Fuse Bits) die vorherigen Daten (EESAVE programmed = 0), deren Position möglicherweise nicht mehr mit der Belegung im aktuellen Programm übereinstimmt oder den Standardwert nach &amp;quot;Chip Erase&amp;quot;: 0xFF (EESAVE unprogrammed = 1). Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Eine besondere Funktion des avr-gcc ist, dass mit entsprechenden Optionen im Makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Variable auf feste Adressen legen ===&lt;br /&gt;
&lt;br /&gt;
Gleich zu Beginn möchte ich darauf hinweisen, dass dieses Verfahren nur ein Workaround ist, mit dem man das Problem der anscheinend &amp;quot;zufälligen&amp;quot; Verteilung&lt;br /&gt;
der EEPROM-Variablen durch den Compiler etwas in den Griff bekommen kann.&lt;br /&gt;
&lt;br /&gt;
Hilfreich kann dies vor allem dann sein, wenn man z.B. über einen Kommandointerpreter (o.ä. Funktionen) direkt bestimmte EEPROM-Adressen manipulieren möchte. Auch wenn man über einen JTAG-Adapter (mk I oder mkII) den Programmablauf manipulieren möchte, indem man die EEPROM-Werte direkt ändert, kann diese Technik hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Im folgenden nun zwei Sourcelistings mit einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.h&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#inlcude &amp;lt;avr/eeprom.h&amp;gt;     // Die EEPROM-Definitionen/Macros der avr-libc einbinden&lt;br /&gt;
&lt;br /&gt;
#define   EESIZE   512      // Maximale Größe des EEPROMS&lt;br /&gt;
&lt;br /&gt;
#define   EE_DUMMY   0x000  // Dummyelement (Adresse 0 sollte nicht genutzt werden)&lt;br /&gt;
#define   EE_VALUE1  0x001  // Eine Bytevariable  &lt;br /&gt;
#define   EE_WORD1L  0x002  // Eine Wordvariable (Lowbyte)&lt;br /&gt;
#define   EE_WORD1H  0x003  // Eine Wordvariable (Highbyte)&lt;br /&gt;
#define   EE_VALUE2  0x004  // Eine weitere Bytevariable&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den Macros &#039;&#039;&#039;#define EE_VALUE1&#039;&#039;&#039; legt man den Namen und die Adresse der&lt;br /&gt;
&#039;Variablen&#039; fest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Die Adressen sollten fortlaufend, zumindest aber aufsteigend sortiert sein! Ansonsten besteht die Gefahr, daß man sehr schnell ein Durcheinander im EEPROM Speicher veranstaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Für den Compiler sind das lediglich Speicher-Adressen, über die auf das EEPROM zugegriffen wird. Der Compiler sieht nichts davon als eine echte Variable an und stößt sich daher auch nicht daran, wenn 2 Makros mit der gleichen Speicheradresse, bzw. überlappenden Speicherbereichen definiert werden. Es liegt einzig und alleine in der Hand des Programmierers, hier keinen Fehler zu machen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
   [EE_DUMMY]   = 0x00,&lt;br /&gt;
   [EE_VALUE1]  = 0x05,&lt;br /&gt;
   [EE_WORD1L]  = 0x01,   &lt;br /&gt;
   [EE_WORD1H]  = 0x00,&lt;br /&gt;
   [EE_VALUE2]  = 0xFF&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch die Verwendung eines Array, welches das gesammte EEPROM umfasst, bleibt&lt;br /&gt;
dem Compiler nicht anderes übrig, als das Array so zu platzieren, dass Element 0&lt;br /&gt;
des Arrays der Adresse 0 des EEPROMs entspricht. (&#039;&#039;Ich hoffe nur, dass die Compilerbauer daran nichts ändern!&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Wie man in dem obigen Codelisting auch sehen kann, hat das Verfahren einen kleinen Haken. Variablen die größer sind als 1 Byte, müssen etwas umständlicher&lt;br /&gt;
definiert werden. Benötigt man keine Initialisierung durch das Programm (was der Normalfall sein dürfte), dann kann man das auch so machen:&lt;br /&gt;
&lt;br /&gt;
Möchte man im EEPROM hintereinander beispielsweise Variablen, mit den Namen &#039;&#039;&#039;Wert&#039;&#039;&#039;, &#039;&#039;&#039;Anzahl&#039;&#039;&#039;, &#039;&#039;&#039;Name&#039;&#039;&#039; und &#039;&#039;&#039;Wertigkeit&#039;&#039;&#039; definieren, wobei Wert und Wertigkeit 1 Byte belegen sollen, Anzahl als 1 Wort (also 2 Bytes) und Name mit 10 Bytes reserviert werden soll, so geht auch folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes Makro definiert also seine Startadresse durch die Startadresse der unmittelbar vorhergehende &#039;Variablen&#039; plus der Anzahl der Bytes die von der vorhergehenden &#039;Variablen&#039; verbraucht werden. Dadurch ist man zumindest etwas auf der sicheren Seite, dass keine 2 &#039;Variablen&#039; im EEPROM überlappend definiert werden. Möchte man eine weitere &#039;Variable&#039; hinzufügen, so wird deren&lt;br /&gt;
Name, einfach anstelle der EE_LAST eingesetzt und eine neue Zeile für EE_LAST eingefügt, in der dann die Größe der &#039;Variablen&#039; festgelegt wird. Zb.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_PROZENT    ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( double ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EE_PROZENT legt die Startadresse für eine neue &#039;Variable&#039; des Datentyps double fest.&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf die EEPROM Werte kann dann z.B.so erfolgen:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t   temp1;&lt;br /&gt;
uint16_t  temp2;&lt;br /&gt;
&lt;br /&gt;
temp1 = eeprom_read_byte(EE_VALUE1);&lt;br /&gt;
temp2 = eeprom_read_word(EE_WORD1L);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ob die in der avr-libc vorhandenen Funktionen dafür verwendet werden können, weiß ich nicht. Aber in einigen Fällen muss man sich sowieso eigene Funktionen&lt;br /&gt;
bauen, welche die spezifischen Anforderungen (Interrupt - Atom Problem, etc.)&lt;br /&gt;
erfüllen.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist nur eine Möglichkeit, wie man dies realisieren kann. Sie bietet einem eine relativ einfache Art die EEPROM-Werte&lt;br /&gt;
auf beliebige Adressen zu legen oder Adressen zu ändern. Die Andere Möglichkeit besteht darin, die EEPROM-Werte wie folgt zu belegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
  0x00,                     //  ee_dummy&lt;br /&gt;
  0x05,                     //  ee_value1&lt;br /&gt;
  0x01,                     //  ee_word1L&lt;br /&gt;
  0x00,                     // (ee_word1H)&lt;br /&gt;
  0xFF                      //  ee_value2&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei kann man Variablen, die größer sind als 1 Byte einfacher definieren und&lt;br /&gt;
man muss nur die Highbyte- oder Lowbyte-Adresse in der &amp;quot;eeprom.h&amp;quot; definieren.&lt;br /&gt;
Allerdings muss man hier höllisch aufpassen, dass man nicht um eine oder mehrere&lt;br /&gt;
Positionen verrutscht!&lt;br /&gt;
&lt;br /&gt;
Welche der beiden Möglichkeiten man einsetzt, hängt vor allem davon ab, wieviele&lt;br /&gt;
Byte, Word und sonstige Variablen man benutzt. Gewöhnen sollte man sich an beide Varianten können ;)&lt;br /&gt;
&lt;br /&gt;
Kleine Schlussbemerkung:&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc unterstützt die Variante 1 und die Variante 2&lt;br /&gt;
* Der icc-avr Compiler unterstützt nur die Variante 2!&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Bei alten Versionen der avr-libc wurden nicht alle AVR Controller  untersützt. Z.B. bei der avr-libc Version 1.2.3 insbesondere bei AVRs &amp;quot;der neuen Generation&amp;quot; (ATmega48/88/168/169) funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register). In neueren Versionen (z.B. avr-libc 1.4.3 aus WinAVR 20050125) wurde die Zahl der unterstüzten Controller deutlich erweitert und eine Methode zur leichten Anpassung an zukünftige Controller eingeführt.&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Will oder kann man nicht auf die neue Version aktualisieren, kann der dort gezeigte Code auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;, gegebenfalls Schutz vor Unterbrechnung/Interrupt ergänzen &#039;&#039;uint8_t sreg; sreg=SREG; cli(); [EEPROM-Code] ; SREG=sreg; return;&#039;&#039;, siehe Abschnitt Interrupts). Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== EEPROM Register ===&lt;br /&gt;
Um das EEPROM anzusteuern sind drei Register von Bedeutung.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEAR Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL da in einem 8 Bit Register keine 512 Adressen adressiert werden können&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEDR Hier werden die Daten eingetragen die geschrieben werden sollen bzw. es enthält die gelesenen Daten&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EECR Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TABLE BORDER=1&amp;gt;&lt;br /&gt;
&amp;lt;TR&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;Bit&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;1&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;0&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&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;Name&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&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;TD&amp;gt;-&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERIE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEMWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERE&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;Read/Write&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;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&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;Init Value&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;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;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;TD&amp;gt;0&amp;lt;/TD&amp;gt;&lt;br /&gt;
&amp;lt;/TR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/TABLE&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bedeutung der Bits:&amp;lt;/U&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4-7 nicht belegt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 3 (EERIE) - EEPROM Ready Interrupt Enable:&amp;lt;/U&amp;gt; Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7) wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0 wird kein Interrupt ausgelöst.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 2(EEMWE) - EEPROM Master Write Enable:&amp;lt;/U&amp;gt; Dieses Bit bestimmt, daß wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE =0 ist und EEWE = 1 gesetzt wird hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst.&lt;br /&gt;
Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 1 (EEWE) - EEPROM Write Enable:&amp;lt;/U&amp;gt; Dieses Bit löst den Schreibvorgang aus wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist wird dieses Bit automatisch wieder auf 0 gesetzt und sofern EERIE gesetzt ist ein Interrupt ausgelöst. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ein Schreibvorgang sieht typischerweise wie folgt aus:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. EEPROM Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Daten übergeben an EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1 &amp;lt;BR&amp;gt;&lt;br /&gt;
5. (Optinal) Warten bis Schreibvorgang abgeschlossen ist.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 0 EERE – EEPROM Read Enable:&amp;lt;/U&amp;gt; Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit wenn das Bit EEWE=0 ist. ISt der LEsevorgang abgeschlossen wird das Bit wieder auf 0 gesetzt und das EEPROM ist für neue Lese/Schreibbefehle wieder bereit.&amp;lt;BR&amp;gt;&lt;br /&gt;
Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. Bereitschaft zum lesen prüfen (EEWE=0)&amp;lt;BR&amp;gt;&lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Lesezyklus auslösen mit EERE = 1&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Warten bis Lesevorgang abgeschlossen EERE = 0&amp;lt;BR&amp;gt;&lt;br /&gt;
5. Daten abholen aus EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bietet sich printf/sprintf an. &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t s[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( s, &amp;quot;Zählerstand: %d&amp;quot;, value );&lt;br /&gt;
    uart_puts( s );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, die Ausgabe stdout auf eigene Ausgabefunktionen umzuleiten. Dazu wird dem Ausgabemechanismus eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben (wie auch immer die Ausgabe stattfindet). Alle anderen, höheren, Funktionen greifen letztendlich auf diese eine Ausgabefunktion zurück, die die Ausgabe eines einzelnen Zeichens vornimmt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uart_init();&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
    printf( &amp;quot;Hello, world!\n&amp;quot; );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil aller printf-Varianten: Sie sind recht speicherintensiv.&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C- und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gnu-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Inline-Assembler: Die Assembleranweisungen werden direkt in den C-Code integriert. Eine Quellcode-Datei enthält somit C- und Assembleranweisungen&lt;br /&gt;
* Assembler-Dateien: Der Assemblercode befindet sich in eigenen Quellcodedateien. Diese werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung (NOP steht für No Operation). Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=Warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
   /* Verzögern der weiteren Programmausführung um&lt;br /&gt;
      genau 3 Taktzyklen */&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeintlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
&lt;br /&gt;
    /* leere Schleife - wird bei eingeschalteter Compiler-Optimierung   wegoptimiert */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* alternative Methode (keine Optimierung): */&lt;br /&gt;
    volatile uint16_t j;&lt;br /&gt;
    for (j = 0; j &amp;lt; 1000; j++)&lt;br /&gt;
      ;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützlicher &amp;quot;Assembler-Einzeiler&amp;quot; ist der Aufruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;);&#039;&#039;), da hierzu in älteren Versionen der avr-libc keine eigene Funktion existiert (in neueren Versionen &#039;&#039;sleep_cpu()&#039;&#039; aus sleep.h).&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise Delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil, dass die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static inline void delayloop16 (uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
    asm volatile (&amp;quot;cp  %A0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;cpc %B0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;breq 2f               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;1:                    \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;sbiw %0,1             \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;brne 1b               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;2:                    &amp;quot;  &lt;br /&gt;
                  : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
	          : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
    );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Anweisung wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen. Der Zeilenumbruch teilt dem Assembler mit, dass ein neuer Befehl beginnt.&lt;br /&gt;
* Als Sprung-Marken (Labels) werden Ziffern verwendet. Diese speziellen Labels sind mehrfach im Code verwendbar. Gesprungen wird jeweils zurück (b) oder vorwärts (f) zum nächsten ausffindbaren Label.&lt;br /&gt;
&lt;br /&gt;
Das Resultat zeigt ein Blick in die Assembler-Datei, die der Compiler mit der option &amp;lt;tt&amp;gt;-save-temps&amp;lt;/tt&amp;gt; nicht löscht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	cp  r24, __zero_reg__ 	 ;  count&lt;br /&gt;
	cpc r25, __zero_reg__ 	 ;  count&lt;br /&gt;
	breq 2f               &lt;br /&gt;
	1:                    &lt;br /&gt;
	sbiw r24,1             	 ;  count&lt;br /&gt;
	brne 1b               &lt;br /&gt;
	2:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc im Abschnitt [http://www.nongnu.org/avr-libc/user-manual/inline_asm.html Related Pages/Inline Asm]. &lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc Deutsche Einführung in Inline-Assembler]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
Wenn man mit dem AVR Studio arbeitet, kann alternativ auch das standardmäßig erstellte Makefile bearbeitet und folgende Zeilen eingefügt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
## Objects that must be built in order to link&lt;br /&gt;
OBJECTS = (alte Dateien...) useful.o&lt;br /&gt;
&lt;br /&gt;
## Compile&lt;br /&gt;
## Hier folgt eine Liste der gelinkten Dateien, darunter einfügen:&lt;br /&gt;
useful.o: ../useful.S&lt;br /&gt;
	$(CC) $(INCLUDES) $(ASMFLAGS) -c  $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Das war es schon. Allerdings gilt es zu beachten, dass das makefile über &amp;quot;Project -&amp;gt; Configuration options&amp;quot; selbst einzubinden ist, sonst wird es natürlich wieder überschrieben.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel eine Funktion &#039;&#039;superFunc&#039;&#039;, die alle Pins des Ports D auf &amp;quot;Ausgang&amp;quot; schaltet, eine Funktion &#039;&#039;ultraFunc&#039;&#039;, die die Ausgänge entsprechend des übergebenen Parameters schaltet, eine Funktion &#039;&#039;gigaFunc&#039;&#039;, die den Status von Port A zurückgibt und eine Funktion &#039;&#039;addFunc&#039;&#039;, die zwei Bytes zu einem 16-bit-Wort addiert. Die Zuweisungen im C-Code (PORTx = ...) verhindern, dass der Compiler die Aufrufe wegoptimiert und dienen nur zur Veranschaulichung der Parameterübergaben.&lt;br /&gt;
&lt;br /&gt;
Zuerst der Assembler-Code. Der Dateiname sei useful.S:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//; Arbeitsregister (ohne &amp;quot;r&amp;quot;) &lt;br /&gt;
workreg  = 16&lt;br /&gt;
workreg2 = 17&lt;br /&gt;
&lt;br /&gt;
//; Konstante:&lt;br /&gt;
ALLOUT = 0xff&lt;br /&gt;
&lt;br /&gt;
//; ** Setze alle Pins von PortD auf Ausgang **&lt;br /&gt;
//; keine Parameter, keine Rückgabe&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg  // beachte: _SFR_IO_ADDR()&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Setze PORTD auf übergebenen Wert **&lt;br /&gt;
//; Parameter in r24 (LSB immer bei &amp;quot;graden&amp;quot; Nummern)&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zustand von PINA zurückgeben **&lt;br /&gt;
//; Rückgabewerte in r24:r25 (LSB:MSB), hier nur LSB genutzt&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zwei Bytes addieren und 16-bit-Wort zurückgeben **&lt;br /&gt;
//; Parameter in r24 (Summand1) und r22 (Summand2) -&lt;br /&gt;
//;  Parameter sind Word-&amp;quot;aligned&amp;quot; d.h. LSB immer auf &amp;quot;graden&amp;quot;&lt;br /&gt;
//;  Registernummern. Bei 8-Bit und 16-Bit Paramtern somit &lt;br /&gt;
//;  beginnend bei r24 dann r22 dann r20 etc.&lt;br /&gt;
//; Rückgabewert in r24:r25&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   push workreg2&lt;br /&gt;
   clr workreg2&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
   pop workreg2&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
//; oh je - sorry - Mein AVR-Assembler ist eingerostet, hoffe das stimmt so...&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Makefile ist der Name der Assembler-Quellcodedatei einzutragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
ASRC = useful.S&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Aufruf erfolgt dann im C-Code so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
extern void superFunc(void);&lt;br /&gt;
extern void ultraFunc(uint8_t setVal);&lt;br /&gt;
extern uint8_t gigaFunc(void);&lt;br /&gt;
extern uint16_t addFunc(uint8_t w1, uint8_t w2);&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
[...]&lt;br /&gt;
  superFunc();&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
&lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
[...]&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird wieder in der lss-Datei ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
   superFunc();&lt;br /&gt;
 148:	0e 94 f6 00 	call	0x1ec&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
 14c:	85 e5       	ldi	r24, 0x55	; 85&lt;br /&gt;
 14e:	0e 94 fb 00 	call	0x1f6&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
 152:	0e 94 fd 00 	call	0x1fa&lt;br /&gt;
 156:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
  &lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
 158:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 15a:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 15c:	0e 94 ff 00 	call	0x1fe&lt;br /&gt;
 160:	8b bb       	out	0x1b, r24	; 27&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
 162:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 164:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 166:	0e 94 fc 00 	call	0x1f8&lt;br /&gt;
 16a:	89 2f       	mov	r24, r25&lt;br /&gt;
 16c:	99 27       	eor	r25, r25&lt;br /&gt;
 16e:	88 bb       	out	0x18, r24	; 24&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
000001ec &amp;lt;superFunc&amp;gt;:&lt;br /&gt;
// setze alle Pins von PortD auf Ausgang&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1ec:	0f 93       	push	r16&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
 1ee:	0f ef       	ldi	r16, 0xFF	; 255&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg&lt;br /&gt;
 1f0:	01 bb       	out	0x11, r16	; 17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 1f2:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 1f4:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001f6 &amp;lt;ultraFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// setze PORTD auf übergebenen Wert&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
 1f6:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
   ret&lt;br /&gt;
 1f8:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fa &amp;lt;gigaFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Zustand von PINA zurückgeben&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
 1fa:	89 b3       	in	r24, 0x19	; 25&lt;br /&gt;
   ret&lt;br /&gt;
 1fc:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fe &amp;lt;addFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// zwei Bytes addieren und 16-bit-Wort zurückgeben&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1fe:	0f 93       	push	r16&lt;br /&gt;
   push workreg2&lt;br /&gt;
 200:	1f 93       	push	r17&lt;br /&gt;
   clr workreg2&lt;br /&gt;
 202:	11 27       	eor	r17, r17&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
 204:	06 2f       	mov	r16, r22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
 206:	08 0f       	add	r16, r24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
 208:	11 1d       	adc	r17, r1&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
 20a:	c8 01       	movw	r24, r16&lt;br /&gt;
   pop workreg2&lt;br /&gt;
 20c:	1f 91       	pop	r17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 20e:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 210:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zuweisung von Registern zu Parameternummer und die Register für die Rückgabewerte sind in den &amp;quot;Register Usage Guidelines&amp;quot; der avr-libc-Dokumentation erläutert.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/assembler.html avr-libc-Dokumentation: Related Pages/avr-libc and assembler programs]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage avr-libc-Dokumentation: Related Pages/FAQ/&amp;quot;What registers are used by the C compiler?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
== Globale Variablen für Datenaustausch ==&lt;br /&gt;
&lt;br /&gt;
Oftmals kommt man um globale Variablen nicht herum, z.B. um den Datenaustausch zwischen Hauptprogramm und Interrupt-Routinen zu realisieren. &lt;br /&gt;
Hierzu muss man im Assembler wissen, wo genau die Variable vom C-Compiler abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
Hierzu muss die Variable, hier &amp;quot;zaehler&amp;quot; genannt, zuerst im C-Code als Global definiert werden, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
int16_t main (void)&lt;br /&gt;
{&lt;br /&gt;
    // irgendein Code, in dem zaehler benutzt werden kann&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im foldenden Assembler-Beispiel wird der Externe Interrupt0  verwendet, um den Zähler hochzuzählen. Es fehlen die Initialisierungen des Interrupts und die Interrupt-Freigabe, so richtig sinnvoll ist der Code auch nicht, aber er zeigt (hoffentlich) wie es geht.&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit Interrupt-Vektoren gilt beim GCC-Assembler das Gleiche, wie bei C: Man muss die exakte Schreibweise beachten, ansonsten wird nicht der Interrupt-Vektor angelegt, sondern eine neue Funktion - und man wundert sich, dass nichts funktionert (vgl. das AVR-GCC-Handbuch).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
.extern zaehler&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      //; wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     //; Status-Register (SREG) sichern!&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     lds temp,zaehler               //; Wert aus dem Speicher lesen&lt;br /&gt;
     inc temp                       //; bearbeiten&lt;br /&gt;
     sts zaehler,temp               //; und wieder zurückschreiben&lt;br /&gt;
&lt;br /&gt;
     pop temp                       //; die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen im Assemblerfile anlegen ===&lt;br /&gt;
&lt;br /&gt;
Alternativ können Variablen aber auch im Assemblerfile angelegt werden. Dadurch kann auf eine .c-Datei verzichtet werden. Für das obige Beispiel könnte der Quelltext dann die Dateien zaehl_asm.S und zaehl_asm.h abgelegt werden, so dass nur noch zaehl_asm.S mit kompiliert werden müsste.&lt;br /&gt;
&lt;br /&gt;
Anstatt im Assemblerfile über das Schlüsselwort &#039;&#039;.extern &#039;&#039; auf eine vorhandene Variable zu verweisen, wird dazu mit dem Schlüsselwort &#039;&#039;.comm&#039;&#039; die benötigte Anzahl von Bytes für eine Variable reserviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.S&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
//; 1 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 1&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Headerdatei wird dann auf die Variable nur noch verwiesen (Schlüsselwort &#039;&#039;extern&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef ZAEHL_ASM_H&lt;br /&gt;
#define ZAEHL_ASM_H&lt;br /&gt;
&lt;br /&gt;
extern volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu globalen Variablen in C werden so angelegte Variablen nicht automatisch mit dem Wert 0 initialisiert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer als 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Variablen, die größer als &#039;&#039;&#039;ein&#039;&#039;&#039; Byte sind, können in Assembler auf ähnliche Art angesprochen werden. Hierzu müssen nur genug Bytes angefordert werden, um die Variable aufzunehmen. Soll z.B. für den Zähler eine Variable vom Typ &#039;&#039;unsigned long&#039;&#039;, also &#039;&#039;uint32_t&#039;&#039; verwendet werden, so müssen 4 Bytes reserviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die dazugehörige Deklaration im Headerfile wäre dann:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
extern volatile uint32_t zaehler;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen, die größer als ein Byte sind, werden die Werte beginnend mit dem niederwertigsten Byte im RAM abgelegt. Das folgende Codeschnippsel zeigt, wie unter Assembler auf die einzelnen Bytes zugegriffen werden kann. Dazu wird im Interrupt nun ein 32-Bit Zähler erhöht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      // wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     // Status-Register (SREG) sichern !&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     // 32-Bit-Zähler incrementieren&lt;br /&gt;
     lds temp, (zaehler + 0)        // 0. Byte (niederwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 0), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
     lds temp, (zaehler + 1)        // 1. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 1), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 2)        // 2. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 2), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 3)        // 3. Byte (höchstwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 3), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
RAUS:&lt;br /&gt;
     pop temp                       // die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TODO:&#039;&#039;&#039; 16-Bit / 32-Bit Variablen, Zugriff auf Arrays (Strings)&lt;br /&gt;
&lt;br /&gt;
= Anhang =&lt;br /&gt;
&lt;br /&gt;
== Besonderheiten bei der Anpassung bestehenden Quellcodes ==&lt;br /&gt;
&lt;br /&gt;
Einige Funktionen, die in frühren Versionen der avr-libc vorhanden waren, werden inzwischen als veraltet angesehen. Sie sind nicht mehr vorhanden oder als &#039;&#039;deprecated&#039;&#039; (missbilligt) ausgewiesen und Definitionen in &amp;lt;compat/deprecated.h&amp;gt; verschoben. Es empfiehlt sich, vorhandenen Code zu portieren und die alten Funktionen nicht mehr zu nutzen, auch wenn diese noch zur Verfügung stehen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zur Deklaration von Interrupt-Routinen ===&lt;br /&gt;
&lt;br /&gt;
Die Funktionen (eigentlich Makros) &#039;&#039;SIGNAL&#039;&#039; und &#039;&#039;INTERRUPT&#039;&#039; zur Deklaration von Interruptroutinen sollten nicht mehr genutzt werden. &lt;br /&gt;
&lt;br /&gt;
In aktuellen Versionen der avr-libc (z.B. avr-libc 1.4.3 aus WinAVR 20060125) werden Interruptroutinen, die &#039;&#039;&#039;nicht&#039;&#039;&#039; durch andere Interrupts &#039;&#039;&#039;unterbrechbar&#039;&#039;&#039; sind, mit ISR deklariert (siehe Abschnitt im Hauptteil). Auch die Benennung wurden vereinheitlicht und an die üblichen Bezeichnungen in den AVR Datenblättern angepasst. In der Dokumentation der avr-libc sind alte und neue Bezeichnungen in der Tabelle gegenübergestellt. Die erforderlichen Schritte zur Portierung:&lt;br /&gt;
&lt;br /&gt;
* #include von avr/signal.h entfernen&lt;br /&gt;
* SIGNAL duch ISR ersetzen&lt;br /&gt;
* Name des Interrupt-Vektors anpassen (SIG_* durch entsprechendes *_vect)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für die Anpassung zuerst ein &amp;quot;alter&amp;quot; Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer2 Output Compare bei einem ATmega8 */&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE2)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt wird der Vektor mit TIMER2 COMP bezeichnet. Die Bezeichnung in der avr-libc entspricht dem Namen im Datenblatt, Leerzeichen werden durch Unterstriche (_) ersetzt und ein _vect angehängt. &lt;br /&gt;
&lt;br /&gt;
Der neue Code sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt; &lt;br /&gt;
/* signal.h entfällt */&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER2_COMP_vect)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Unklarheiten bezüglich der neuen Vektorlabels hilft (noch) ein Blick in die Headerdatei des entsprechenden Controllers. Für das vorherige Beispiel also der Blick in die Datei iom8.h für den ATmega8, dort findet man die veraltete Bezeichnung unterhalb der aktuellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect		_VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2		_VECTOR(3)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Nachfolgendes mit avr-libc 1.4.5 (in WinAVR 1/2007 behoben - noch eine Weile auskommentiert lassen und dann löschen.&lt;br /&gt;
Konnte in alten Versionen signal.h ohne interrupt.h eingebunden werden, erhält man bei Verwendung der avr-libc Version 1.4.3 (WinAVR 2/2005) beim Compilieren eine Fehlermeldung, da mit signal.h nicht die erforderlichen Definitionen eingebunden werden. Der Lösungsvorschlag in signal.h auch interrupt.h einzubinden, wurde von den avr-libc-Enwicklern akzeptiert und das Problem ist  im Quellcode (CVS) bereits behoben. Es ist aber noch keine avr-libc-&amp;quot;Release&amp;quot; bzw. noch kein WinAVR mit dieser avr-libc-Korrektur verfügbar (Stand 5.2.2006). Will oder kann man den Quellcode nicht aktualisieren, gibt es folgende Alternativen:&lt;br /&gt;
* in Quellcodedateien, in denen nur avr/signal.h eingebunden wird, interrupt.h einbinden (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;). signal.h weiterhin einbinden, falls Kompatibiltät mit alten Versionen gewünscht ist.&lt;br /&gt;
* in der Datei signal.h (bein WinAVR in c:/WinAVR/avr/include/avr/signal.h) ein (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;) ergänzen --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;unterbrechbare&#039;&#039;&#039; Interruptroutinen, die mittels &#039;&#039;INTERRUPT&#039;&#039; deklariert sind, gibt es keinen direkten Ersatz in Form eines Makros. Solche Routinen sind laut Dokumentation der avr-libc in folgender Form zu deklarieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* ** alt ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
INTERRUPT(SIG_OVERFLOW0)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ** neu: ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void TIMER0_OVF_vect(void) __attribute__((interrupt));&lt;br /&gt;
void TIMER0_OVF_vect(void) &lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von &#039;&#039;INTERRUPT&#039;&#039; die Header-Datei &#039;&#039;compat/deprecated.h&#039;&#039; einzubinden. Man sollte bei dieser Gelegenheit jedoch nochmals überprüfen, ob die Funktionalität von &#039;&#039;INTERRUPT&#039;&#039; tatsächlich gewollt ist. In vielen Fällen wurde &#039;&#039;INTERRUPT&#039;&#039; dort genutzt, wo eigentlich &#039;&#039;SIGNAL&#039;&#039; (nunmehr &#039;&#039;ISR&#039;&#039;) hätte genutzt werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;inp&#039;&#039; und &#039;&#039;outp&#039;&#039; zum Einlesen bzw. Schreiben von Registern sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
unsigned char i, j;&lt;br /&gt;
&lt;br /&gt;
// alt:&lt;br /&gt;
  i = inp(PINA);&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  outp(PORTB, j);&lt;br /&gt;
&lt;br /&gt;
// neu (nicht mehr wirklich neu...):&lt;br /&gt;
  i = PINA&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  PORTB = j;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von inp und outp die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Zugriff auf Bits in Registern ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;cbi&#039;&#039; und &#039;&#039;sbi&#039;&#039; zum Löschen und Setzen von Bits sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg. Die Bezeichnung ist ohnehin irreführend da die Funktionen nur für Register mit Adressen im unteren Speicherbereich tatsächlich in die Assembleranweisungen cbi und sbi übersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// alt:&lt;br /&gt;
  sbi(PORTB, PB2);&lt;br /&gt;
  cbi(PORTC, PC1);&lt;br /&gt;
&lt;br /&gt;
// neu (auch nicht mehr wirklich neu...):&lt;br /&gt;
  PORTB |=  (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
  PORTC &amp;amp;= ~(1&amp;lt;&amp;lt;PC1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von sbi und cbi die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden. Wer unbedingt will, kann sich natürlich eigene Makros mit aussagekräftigeren Namen definieren. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &amp;amp;= ~(1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Selbstdefinierte (nicht-standardisierte) ganzzahlige Datentypen ===&lt;br /&gt;
&lt;br /&gt;
Bei den im Folgenden genannten Typdefinitionen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich, bei der Überarbeitung von altem Code die im Abschnitt &#039;&#039;standardisierten ganzzahligen Datentypen&#039;&#039; beschriebenen Datentypen zu nutzen (stdint.h) und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef unsigned char      BYTE;       // besser: uint8_t  aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned short     WORD;       // besser: uint16_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long      DWORD;      // besser: uint32_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long long QWORD;      // besser: uint64_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; BYTE : Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 255. &lt;br /&gt;
&lt;br /&gt;
; WORD : Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 65535. &lt;br /&gt;
&lt;br /&gt;
; DWORD : Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
; QWORD : Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Funktionen im Makefile ==&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken (Libraries/.a-Dateien) hinzufügen ===&lt;br /&gt;
&lt;br /&gt;
Um Funktionen aus Bibliotheken (&amp;quot;echte&amp;quot; Libraries, *.a-Dateien) zu nutzen, sind dem Linker die Namen der Bibliotheken als Parameter zu übergeben. Dazu ist die Option -l (kleines L) vorgesehen, an die der Name der Library angehängt wird. &lt;br /&gt;
&lt;br /&gt;
Dabei ist zu beachten, dass der Name der Library und der Dateiname der Library nicht identisch sind. Der hinter -l angegebene Name entspricht dem Dateinamen der Library ohne die Zeichenfolge &#039;&#039;lib&#039;&#039; am Anfang des Dateinamens und ohne die Endung &#039;&#039;.a&#039;&#039;. Sollen z.B. Funktionen aus einer Library mit dem Dateinamen &#039;&#039;libefsl.a&#039;&#039; eingebunden (gelinkt) werden, lautet der entsprechende Parameter -lefsl (vergl. auch -lm zum Anbinden von libm.a). &lt;br /&gt;
&lt;br /&gt;
In Makefiles wird traditonell eine make-Variable LDLIBS genutzt, in die &amp;quot;l-Parameter&amp;quot; abgelegt werden. Die WinAVR-makefile-Vorlage enthält diese Variable zwar nicht, dies stellt jedoch keine Einschränkung dar, da alle in der make-Variable LDFLAGS abgelegten Parameter an den Linker weitergereicht werden. &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Einbinden von Funktionen aus einer Library efsl (Dateiname libefsl.a)&lt;br /&gt;
LDFLAGS += -lefsl&lt;br /&gt;
# Einbinden von Funktionen aus einer Library xyz (Dateiname libxyz.a)&lt;br /&gt;
LDFLAGS += -lxyz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Liegen die Library-Dateien nicht im Standard Library-Suchpfad, sind die Pfade mittels Parameter &#039;&#039;-L&#039;&#039; ebenfalls anzugeben. (Der vordefinierte Suchpfad kann mittels &#039;&#039;avr-gcc --print-search-dirs&#039;&#039; angezeigt werden.)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Projekt (&amp;quot;superapp2&amp;quot;), in dem der Quellcode von zwei Libraries (efsl und xyz) und der Quellcode der eigentlichen Anwendung in verschiedenen Verzeichnissen mit der folgenden &amp;quot;Baumstruktur&amp;quot; abgelegt sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
superapp2&lt;br /&gt;
|&lt;br /&gt;
+----- efslsource (darin libefsl.a)&lt;br /&gt;
|&lt;br /&gt;
+----- xyzsource (darin libxyz.a)&lt;br /&gt;
|&lt;br /&gt;
+----- firmware (darin Anwendungs-Quellcode und Makefile)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt, dass im Makefile die Verzeichnis efslsource und xyzsource in den Library-Suchpfad aufzunehmen sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
LDFLAGS += -L../efslsource/ -L../xyzsource/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fuse-Bits ===&lt;br /&gt;
&lt;br /&gt;
Zur Berechnung der Fuse-Bits bietet sich neben dem Studium des Datenblattes auch der [http://palmavr.sourceforge.net/cgi-bin/fc.cgi AVR Fuse Calculator] an. Gewarnt werden muss vor der Benutzung von PonyProg, weil dort durch die negierte Darstellung gern Fehler gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Soll die Programmierung von Fuse- und Lockbits automatisiert werden, kann man dies ebenfalls durch Einträge im Makefile vornehmen, die beim Aufruf von &amp;quot;make program&amp;quot; an die genutzte Programmiersoftware übergeben werden. In der makefile-Vorlage von WinAVR (und mfile) gibt es dafuer jedoch keine &amp;quot;Ausfüllhilfe&amp;quot; (Stand 9/2006). Die folgenden Ausführungen gelten für die Programmiersoftware [[AVRDUDE]] (Standard in der WinAVR-Vorlage), können jedoch sinngemäß auf andere Programmiersoftware übertragen werden, die die Angabe der Fuse- und Lockbits-Einstellungen per Kommandozeilenparameter unterstützt (z.B. stk500.exe). Im einfachsten Fall ergänzt man im Makefile einige Variablen, deren Werte natürlich vom verwendeten Controller und den gewünschten Einstellungen abhängen (vgl. Datenblatt Fuse-/Lockbits):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#---------------- Programming Options (avrdude) ----------------&lt;br /&gt;
&lt;br /&gt;
#...&lt;br /&gt;
#Beispiel! f. ATmega16 - nicht einfach uebernehmen! Zahlenwerte anhand&lt;br /&gt;
#--------- des Datenblatts nachvollziehen und gegebenenfalls aendern.&lt;br /&gt;
#&lt;br /&gt;
AVRDUDE_WRITE_LFUSE = -U lfuse:w:0xff:m&lt;br /&gt;
AVRDUDE_WRITE_HFUSE = -U hfuse:w:0xd8:m&lt;br /&gt;
AVRDUDE_WRITE_LOCK  = -U lock:w:0x2f:m&lt;br /&gt;
#...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit diese Variablen auch genutzt werden, ist der Aufruf von avrdude im Makefile entsprechend zu ergänzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Program the device.  &lt;br /&gt;
program: $(TARGET).hex $(TARGET).eep&lt;br /&gt;
# ohne Fuse-/Lock-Einstellungen (nach WinAVR Vorlage Stand 4/2006)&lt;br /&gt;
#	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
#        $(AVRDUDE_WRITE_EEPROM)&lt;br /&gt;
# mit Fuse-/Lock-Einstellungen&lt;br /&gt;
        $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_LFUSE) \&lt;br /&gt;
        $(AVRDUDE_WRITE_HFUSE) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
        $(AVRDUDE_WRITE_EEPROM) $(AVRDUDE_WRITE_LOCK)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weiter Möglichkeit besteht darin, die Fuse- und Lockbit-Einstellungen vom Preprozessor/Compiler generieren zu lassen. Die Fuse-Bits werden dann bei Verwendung von AVRDUDE in eigene Hex-Files geschrieben. Hierzu kann man z.B. folgendes Konstrukt verwenden:&lt;br /&gt;
&lt;br /&gt;
In eine der C-Sourcen wird eine Variable je Fuse-Byte vom Typ &#039;&#039;unsigned char&#039;&#039; deklariert und in eine extra Section gepackt. Dies kann entweder in einem vorhandenen File passieren oder in ein neues (z.B. fuses.c) geschrieben werden. Das File muss im Makefile aber auf jeden Fall mit kompiliert und gelinkt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// tiny 2313 fuses low byte&lt;br /&gt;
#define CKDIV8  7&lt;br /&gt;
#define CKOUT   6&lt;br /&gt;
#define SUT1    5&lt;br /&gt;
#define SUT0    4&lt;br /&gt;
#define CKSEL3  3&lt;br /&gt;
#define CKSEL2  2&lt;br /&gt;
#define CKSEL1  1&lt;br /&gt;
#define CKSEL0  0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses high byte&lt;br /&gt;
#define DWEN       7&lt;br /&gt;
#define EESAVE     6&lt;br /&gt;
#define SPIEN      5&lt;br /&gt;
#define WDTON      4&lt;br /&gt;
#define BODLEVEL2  3&lt;br /&gt;
#define BODLEVEL1  2&lt;br /&gt;
#define BODLEVEL0  1&lt;br /&gt;
#define RSTDISBL   0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses extended byte&lt;br /&gt;
#define SELFPRGEN  0&lt;br /&gt;
&lt;br /&gt;
#define LFUSE         __attribute__ ((section (&amp;quot;lfuses&amp;quot;)))&lt;br /&gt;
#define HFUSE         __attribute__ ((section (&amp;quot;hfuses&amp;quot;)))&lt;br /&gt;
#define EFUSE         __attribute__ ((section (&amp;quot;efuses&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// select ext crystal 3-8Mhz&lt;br /&gt;
unsigned char lfuse LFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;CKDIV8) | (1&amp;lt;&amp;lt;CKOUT) | (1&amp;lt;&amp;lt;CKSEL3) | (1&amp;lt;&amp;lt;CKSEL2) | &lt;br /&gt;
      (0&amp;lt;&amp;lt;CKSEL1) | (1&amp;lt;&amp;lt;CKSEL0) | (0&amp;lt;&amp;lt;SUT1) | (1&amp;lt;&amp;lt;SUT0) );&lt;br /&gt;
unsigned char hfuse HFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;DWEN) | (1&amp;lt;&amp;lt;EESAVE) | (0&amp;lt;&amp;lt;SPIEN) | (1&amp;lt;&amp;lt;WDTON) | &lt;br /&gt;
      (1&amp;lt;&amp;lt;BODLEVEL2) | (1&amp;lt;&amp;lt;BODLEVEL1) | (0&amp;lt;&amp;lt;BODLEVEL0) | (1&amp;lt;&amp;lt;RSTDISBL) );&lt;br /&gt;
unsigned char efuse EFUSE =&lt;br /&gt;
    ((0&amp;lt;&amp;lt;SELFPRGEN));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG: Die Bitpositionen wurden nicht vollständig getestet!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Eine &amp;quot;1&amp;quot; bedeutet hier, dass das Fuse-Bit &#039;&#039;nicht&#039;&#039; programmiert wird - die Funktion also i.A. nicht aktiviert ist. Eine &amp;quot;0&amp;quot; hingegen aktiviert die meisten Funktionen. Dies ist wie im Datenblatt (1 = unprogrammed, 0 = programmed).&lt;br /&gt;
&lt;br /&gt;
Das Makefile muss nun noch um folgende Targets erweitert werden (mit Tabulator einrücken - nicht mit Leerzeichen):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
lfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j lfuses --change-section-address lfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-lfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-lfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U lfuse:w:$(TARGET)-lfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
hfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j hfuses --change-section-address hfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-hfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-hfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U hfuse:w:$(TARGET)-hfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
efuses: build&lt;br /&gt;
        -$(OBJCOPY) -j efuses --change-section-address efuses=0 \&lt;br /&gt;
         -O ihex $(TARGET).elf $(TARGET)-efuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-efuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U efuse:w:$(TARGET)-efuse.hex;&lt;br /&gt;
        fi;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Target &amp;quot;clean&amp;quot; muss noch um die Zeilen&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
        $(REMOVE) $(TARGET)-lfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-hfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-efuse.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
erweitert werden, wenn auch die Fuse-Dateien gelöscht werden sollen.&lt;br /&gt;
&lt;br /&gt;
Um nun die Fusebits des angeschlossenen Controllers zu programmieren muss lediglichein &amp;quot;make lfuses&amp;quot;, &amp;quot;make hfuses&amp;quot; oder &amp;quot;make efuses&amp;quot; gestartet werden.&lt;br /&gt;
Bei den Fuse-Bits ist besondere Vorsicht geboten, da diese das Programmieren des Controllers unmöglich machen können. Also erst programmieren, wenn man einen HV-Programmierer hat oder ein paar Reserve-AVRs zur Hand ;-)&lt;br /&gt;
&lt;br /&gt;
Um weiterhin den &amp;quot;normalen&amp;quot; Flash beschreiben zu können, ist es wichtig, für das Target &amp;quot;*.hex&amp;quot; im Makefile nicht nur &amp;quot;-R .eeprom&amp;quot; als Parameter zu übergeben sondern zusätzlich noch &amp;quot;-R lfuses -R efuses -R hfuses&amp;quot;. Sonst bekommt AVRDUDE Probleme diese Sections in den Flash (wo sie ja nicht hingehören) zu schreiben.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[AVR_Fuses#Vergleich_der_Fuses_bei_verschiedenen_Programmen|Vergleich der Fuses bei verschiedenen Programmen]]&lt;br /&gt;
&lt;br /&gt;
== Externe Referenzspannung des internen Analog-Digital-Wandlers ==&lt;br /&gt;
&lt;br /&gt;
Die minimale (externe) Referenzspannung des ADC darf nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. z.B. beim ATMEGA8 darf sie laut Datenblatt (S.245, Tabelle 103, Zeile &amp;quot;VREF&amp;quot;) 2,0V nicht unterschreiten. HINWEIS: diese Information findet sich erst in der letzten Revision (Rev. 2486O-10/04) des Datenblatts.&lt;br /&gt;
&lt;br /&gt;
Meiner &amp;lt;!-- Wer? - es gibt inzwischen x Leute die mehr oder weniger viel in diesem Artikel geschrieben haben --&amp;gt; eigenen Erfahrung nach kann man aber (auf eigene Gefahr und natürlich nicht für Seriengeräte) durchaus noch ein klein wenig weiter heruntergehen, bei dem von mir unter die Lupe genommenen ATMEGA8L (also die Low-Voltage-Variante) funktioniert der ADC bei 5V Betriebsspannung mit bis zu VREF=1,15V hinunter korrekt, ab 1,1V und darunter digitalisiert er jedoch nur noch Blödsinn). Ich würde sicherheitshalber nicht unter 1,5V gehen und bei niedrigeren Betriebsspannungen mag sich die Untergrenze für VREF am Pin AREF ggf. nach oben&#039;&#039;&#039;(!)&#039;&#039;&#039; verschieben.&lt;br /&gt;
&lt;br /&gt;
In der letzten Revision des Datenblatts ist außerdem korrigiert, dass ADC4 und ADC5 sehr wohl 10 Bit Genauigkeit bieten (und nicht bloß 8 Bit, wie in älteren Revisionen irrtümlich angegeben.)&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* stdio.h, malloc() &lt;br /&gt;
* Code-Optimierungen (&amp;quot;tricks&amp;quot;), siehe auch Application Note [http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf AVR035: Efficient C Coding for AVR]&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
* SPI siehe [http://www.uni-koblenz.de/~physik/informatik/MCU/SPI.pdf SPI Bus mit Atmel AVR]&lt;br /&gt;
* I²C / TWI Bus [http://www.roboternetz.de/wissen/index.php/TWI]&lt;br /&gt;
* Bootloader (bez. auf boot.h)&lt;br /&gt;
* CAN-Bus&lt;br /&gt;
* Einsatz von einfachen Betriebssystemen auf dem AVR&lt;br /&gt;
* Übersicht zu den C bzw. GCC-predefined Makros (__DATE__, __TIME__,...)&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
* ADC ; &lt;br /&gt;
* Timer&lt;br /&gt;
* USB ; Steuerung mit USB&lt;br /&gt;
* Multiplexen Siebensegment&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28254</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28254"/>
		<updated>2008-05-27T17:05:58Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Ganzzahlige (Integer) Datentypen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[AVR-GCC]] erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Progammiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong]. Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern, weder in Assembler noch in einer anderen Sprache. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR Controllern finden. Bei WinAVR gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden stetig weiterentwickelt. Erläuterungen und Beispiele beziehen sich auf den C-Compiler avr-gcc ab Version 3.4 und die avr-libc ab Version 1.4.3. Die Unterschiede zu älteren Versionen werden im Haupttext und Anhang zwar erläutert, Anfängern sei jedoch empfohlen, die aktuellen Versionen zu nutzen (für MS-Windows: aktuelle Version des [[WinAVR]]-Pakets). &lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in PDF-Form erhältlich bei:&lt;br /&gt;
http://www.siwawi.arubi.uni-kl.de/avr_projects/AVR-GCC-Tutorial_-_www_mikrocontroller_net.pdf&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels avr-gcc/avr-libc zu erstellen und zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR Controllers, der vom avr-gcc Compiler unterstützt wird (alle ATmegas und die meisten AT90, siehe Dokumentation der avr-libc für unterstützte Typen). Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man [[AVR#Starterkits|hier]].&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc Compiler und die avr-libc. Kostenlos erhältlich für nahezu alle Plattformen und Betriebssysteme. Für MS-Windows im Paket [[WinAVR]]; für Unix/Linux siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]]).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder nur die eigenen C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
&lt;br /&gt;
* Die [[AVR Checkliste]] durcharbeiten.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/2 GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  auf den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
&lt;br /&gt;
* Die Beispieleprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweisen sind aber auch auf C-Programme übertragbar.&lt;br /&gt;
&lt;br /&gt;
* Einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien, um das Problem nachzuvollziehen, sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z. B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.&amp;amp;nbsp;B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.&amp;amp;nbsp;B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen, um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu zwei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer integrierten Entwicklungsumgebung (IDE = &#039;&#039;&#039;I&#039;&#039;&#039;ntegrated &#039;&#039;&#039;D&#039;&#039;&#039;evelopment &#039;&#039;&#039;E&#039;&#039;&#039;nvironment), bei der alle Einstellungen z.&amp;amp;nbsp;B. in Dialogboxen durchgeführt werden können. Unter Anderem kann AVRStudio ab Version 4.12 (kostenlos auf [http://www.atmel.com/ atmel.com]) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs für den avr-gcc (ohne Anspruch auf Vollständigkeit): AtmanAvr C (relativ günstig), KamAVR (kostenlos), VMLab (ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.&amp;amp;nbsp;B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand der universellen und plattformunabhängigen Vorgehensweise mittels make und Makefiles näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind lediglich Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xff;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5a)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/;  // (5b)&lt;br /&gt;
   }                         // (5c)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (6)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In der mit (1) markierten Zeile wird eine so genannte Header-Datei eingebunden. In io.h sind die Registernamen definiert, die im späteren Verlauf genutzt werden.&lt;br /&gt;
&lt;br /&gt;
* Bei (2) beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion main.&lt;br /&gt;
&lt;br /&gt;
* Die Anschlüsse eines AVR (&amp;quot;Beinchen&amp;quot;) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Richtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B zu Ausgängen.&lt;br /&gt;
&lt;br /&gt;
* (4) stellt die Werte der Ausgänge ein. Die den ersten beiden Bits des Ports zugeordneten Anschlüsse (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential).&lt;br /&gt;
&lt;br /&gt;
* (5) ist die so genannte Hauptschleife (main-loop). Dies ist eine Programmschleife, welche kontinuierlich wiederkehrende Befehle enthält. In diesem Beispiel ist sie leer. Der Controller durchläuft die Schleife immer wieder, ohne dass etwas passiert (außer das Strom verbraucht wird). Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Ohne diese Schleife wäre der Zustand des Controllers nach dem Programmende undefiniert.&lt;br /&gt;
&lt;br /&gt;
* (6) wäre das Programmende. Die Zeile ist nur aus Gründen der C-Kompatibilität enthalten: int main(void) besagt, dass die Funktion einen Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein auf dem Controller lauffähiges Programm zu übersetzen, wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen Makefile (ohne Endung) im selben Verzeichnis, in dem auch die Datei main.c mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im folgenden Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\tmp\gcc_tut\quickstart&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei main.hex, in der der Code für den AVR enthalten ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.&amp;amp;nbsp;B. über In-System-Programming (ISP) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann (z.&amp;amp;nbsp;B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio), kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine LED leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode mit einem dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Exkurs: Makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebungen à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Plattformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt, genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.com) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das Makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den im Makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im Makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten Makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt [[AVR-GCC-Tutorial#EEPROM|Speicherzugriffe]]), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendeten Controllers gesetzt. Eine Liste der von avr-gcc und der avr-libc unterstützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien eintragen ==&lt;br /&gt;
&lt;br /&gt;
Der Name der Quellcodedatei, welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.&amp;amp;nbsp;B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
# nicht auskommentiert: EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Das erstellte Makefile und der Code müssen im gleichen Ordner sein, auch sollte der Dateiname nicht verändert werden. &lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem Makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschinencode erzeugt. Beim Update auf eine neue Compilerversion ist zu beachten, dass diese möglicherweise intern andere Optimierungsalgorithmen verwendet und sich dadurch die Größe des Machinencodes etwas ändert, ohne dass man im Quellcode etwas geändert hat.&lt;br /&gt;
&lt;br /&gt;
Als Orientierungswerte die Größe des Maschinencodes bei verschiedenen Optionen für einen nicht näher spezifizierten relativ kleinen Testcode bei Verwendung einer nicht näher spezifizierten Compilerversion. &lt;br /&gt;
&lt;br /&gt;
* -O0 : 12&#039;217 Byte&lt;br /&gt;
&lt;br /&gt;
* -O1 : 9&#039;128 Byte&lt;br /&gt;
&lt;br /&gt;
* -O2 : 1&#039;670 Byte&lt;br /&gt;
&lt;br /&gt;
* -O3 : 3&#039;004 Byte&lt;br /&gt;
&lt;br /&gt;
* -Os : 1&#039;695 Byte&lt;br /&gt;
&lt;br /&gt;
Im diesem Testfall führt die Option -O2 mit zum kompaktesten Code, dies  allerdings hier nur mit 25 Bytes &amp;quot;Vorsprung&amp;quot;. Es kann durchaus sein, dass nur wenige Programmerweiterungen dazu führen, dass Compilieren mit -Os wieder in kompakteren Code resultiert.&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/using_tools.html#gcc_optO avr-libc manual Abschnitt Using the gnu-tools/Compiler-Optionen]&lt;br /&gt;
&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_optflags avr-libc Manual FAQ Nr. 16] (Stand avr-libc Version 1.4.5)&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wird hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (großes S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten, lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Taktfrequenz ===&lt;br /&gt;
&lt;br /&gt;
Neuere Versionen der WinAVR/Mfile Vorlage für Makefiles beinhalten die Definition einer Variablen F_CPU (WinAVR 2/2005). Darin wird die Taktfrequenz des Controllers in Hertz eingetragen. Die Definition steht dann im gesamten Projekt ebenfalls unter der Bezeichnung F_CPU zur Verfügung (z.&amp;amp;nbsp;B. um daraus UART-, SPI- oder ADC-Frequenzeinstellungen abzuleiten).&lt;br /&gt;
&lt;br /&gt;
Die Angabe hat rein &amp;quot;informativen&amp;quot; Charakter, die tatsächliche Taktrate wird über den externen Takt (z.&amp;amp;nbsp;B. Quarz) bzw. die Einstellung des internen R/C-Oszillators  bestimmt. Die Nutzung von F_CPU hat also nur Sinn, wenn die Angabe mit dem tatsächlichen Takt übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Innerhalb neuerer Versionen der avr-libc (ab Version 1.2) wird die Definition der Taktfrequenz (F_CPU) zur Berechnung der Wartefunktionen in delay.h genutzt. Diese funktionieren nur dann korrekt, wenn F_CPU mit der tatsächlichen Taktfrequenz übereinstimmt.&lt;br /&gt;
F_CPU muss dazu jedoch nicht unbedingt im makefile definiert werden. Es reicht aus, wird aber bei mehrfacher Anwendung unübersichtlich, vor &#039;&#039;#include &amp;lt;util/delay.h&amp;gt;&#039;&#039; (veraltet: &#039;&#039;#include &amp;lt;avr/delay.h&amp;gt;&#039;&#039;) ein &#039;&#039;#define F_CPU [hier Takt in Hz]UL&#039;&#039; einzufügen. Bei Nutzung von delay.h ist darauf zu achten, dass die Optimierung des Compilers nicht ausgeschaltet ist, sonst wird sehr viel Code erzeugt und die Wartezeit stimmt nicht mit der gewünschten Zeitspanne überein. Vgl. dazu den [http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html entsprechenden Abschnitt der Dokumentation].&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage so genannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* Im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
* Die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* Weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff: (sollte nur noch in Ausnahmefällen genutzt werden)&lt;br /&gt;
&lt;br /&gt;
* Im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
* Die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* Weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler diese &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, einem Nachbau davon (BootICE, Evertool o.&amp;amp;nbsp;ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
Die Verwendung von Makefiles bietet noch viele weitere Möglichkeiten, einige davon werden im Anhang [[AVR-GCC-Tutorial#Zus.C3.A4tzliche_Funktionen_im_Makefile|Zusätzliche Funktionen im Makefile]] erläutert.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige (Integer) Datentypen =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei stdint.h definiert. &lt;br /&gt;
Zur Nutzung der standardisierten Typen bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// ab avr-libc Version 1.2.0 möglich und empfohlen:&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// veraltet: #include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef signed char        int8_t;&lt;br /&gt;
typedef unsigned char      uint8_t;&lt;br /&gt;
&lt;br /&gt;
typedef short              int16_t;&lt;br /&gt;
typedef unsigned short     uint16_t;&lt;br /&gt;
&lt;br /&gt;
typedef long               int32_t;&lt;br /&gt;
typedef unsigned long      uint32_t;&lt;br /&gt;
&lt;br /&gt;
typedef long long          int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* int8_t steht für einen 8-Bit Integer mit einem Wertebereich -128 bis +127.&lt;br /&gt;
&lt;br /&gt;
* uint8_t steht für einen 8-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 255&lt;br /&gt;
&lt;br /&gt;
* int16_t steht für einen 16-Bit Integer mit einem Wertebereich -32768 bis +32767.&lt;br /&gt;
&lt;br /&gt;
* uint16_t steht für einen 16-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 65535.&lt;br /&gt;
&lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; werden als vorzeichenbehaftete Zahlen abgespeichert. Typen mit vorgestelltem &#039;&#039;u&#039;&#039; dienen der Ablage von postiven Zahlen (inkl. 0). Siehe dazu auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/(Standard) Integer Types.&lt;br /&gt;
&lt;br /&gt;
= Bitfelder =&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&lt;br /&gt;
&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in eine einzelne Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitfelder sparen Platz im RAM, zu Lasten von Platz im Flash, verschlechtern aber unter Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.&lt;br /&gt;
&lt;br /&gt;
Wenn man nur eine paar wenige Variablen vom Typ bool verwenden möchte, kann man&lt;br /&gt;
auch die Headerdatei &amp;lt;stdbool.h&amp;gt; einbinden und sich dann wie gewohnt einen Booltyp anlegen. Variablen dieses Typs brauchen dennoch 1 Byte Speicher, ermöglichen aber eine genaue Unterscheidung zwischen Zahlenvariable und boolscher Variable.&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;C-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum können für allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden&lt;br /&gt;
AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039;, wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output).  Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt.&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend&lt;br /&gt;
angepasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O-Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek (avr-libc) stellt auch Funktionen zur Abfrage eines einzelnen Bits eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist, wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind &#039;&#039;nicht erforderlich&#039;&#039;, man kann auch &amp;quot;einfache&amp;quot; C-Syntax verwenden, die universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) wenn das Bit gesetzt und 0 (&amp;quot;falsch&amp;quot;) wenn es nicht gesetzt ist. Die Negierung des Ausdrucks, also  &#039;&#039;&#039;!&#039;&#039;&#039;(Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)), entspricht &#039;&#039;bit_is_clear&#039;&#039; und gibt einen Wert &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) zurück, falls das Bit nicht gesetzt ist,&lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O-Register einfach wie eine Variable setzen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und outp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben von Bits ====&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausdruck:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit (für 1) eines Bytes hat die Bitnummer 0, das &amp;quot;höchstwertige&amp;quot; (für 128) die Nummer 7.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );    /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3);   /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die warten, bis ein bestimmter Zustand auf einem Bit erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend gewartet&amp;quot; wird. D.h., der Programmablauf bleibt an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den Watchdog ein, muss man darauf achten, dass dieser auch noch getriggert wird (zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Änderung: Habe PINx und PORTx vertauscht.PINx ist für Pull up und PORTx für high bzw low. Michael.L  - &lt;br /&gt;
&lt;br /&gt;
Was sollte diese Aenderung?? Den Rest des Abschnitts auch gelesen? Evtl. die hier nicht erlaeuterten Zusatzfunktion der neuen AVRs durcheinandergeworfen? Aenderung verworfen bis mehr Details.  m.thomas. &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
// Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
&lt;br /&gt;
DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
/* mehr Tipparbeit aber übersichtlicher: */&lt;br /&gt;
DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&lt;br /&gt;
/* Einige Compiler erlauben die Eingabe von Konstanten im &lt;br /&gt;
   Binärformat, z.B. gcc in WinAVR, NICHT aber aus&lt;br /&gt;
   GNU-gcc-Quellen selbst erstellte Compiler. Diese Schreib-&lt;br /&gt;
   weise also nicht nutzen, wenn Code mit anderen &lt;br /&gt;
   ausgetauscht werden soll. */&lt;br /&gt;
DDRB = 0b00011111;    /* direkte Zuweisung &lt;br /&gt;
                         - übersichtlicher &lt;br /&gt;
                         - nicht standardkonform &lt;br /&gt;
                         - selten portabel&lt;br /&gt;
                         - nur bei modifiziertem GCC */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
Weitere Beispiele:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
DDRB = 0xff; &lt;br /&gt;
// Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; DDB0 );&lt;br /&gt;
// Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( ( 1 &amp;lt;&amp;lt; DDB3 ) | ( 1&amp;lt;&amp;lt;DDB4) );&lt;br /&gt;
// Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB |= ( 1 &amp;lt;&amp;lt; DDB0) | ( 1 &amp;lt;&amp;lt; DDB3 );&lt;br /&gt;
// Alle Pins auf Eingang:&lt;br /&gt;
DDRB = 0x00;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&#039;&#039;&lt;br /&gt;
* &#039;&#039;zuerst die Bits im PORTx-Register setzen und&#039;&#039;&lt;br /&gt;
* &#039;&#039;anschließend die Datenrichtung auf Ausgang stellen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Daraus ergibt sich die Abfolge für einen Eingangspin:&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze PORTx: interner Pull-Up aktiv&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze DDRx: Ausgang &amp;quot;high&amp;quot;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bei der Reihenfolge erst DDRx und dann PORTx, kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;&#039;nicht&#039;&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, &#039;&#039;&#039;sondern&#039;&#039;&#039; Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.&amp;lt;/font&amp;gt; (Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände.)&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch.  Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVRs haben sogar an den meisten Pins softwaremäßig zuschaltbare interne Pull-Up Widerstände, welche wir natürlich auch verwenden können.&lt;br /&gt;
&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;DDC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== (Tasten-)Entprellung ====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim Schließen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&lt;br /&gt;
&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen als mehrfache Impulse gezählt wird. Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* bei alter avr-libc: #include &amp;lt;avr/delay.h&amp;gt; */      &lt;br /&gt;
&lt;br /&gt;
/* Einfache Funktion zum Entprellen eines Tasters */&lt;br /&gt;
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)&lt;br /&gt;
{&lt;br /&gt;
    if ( ! (*port &amp;amp; (1 &amp;lt;&amp;lt; pin)) )&lt;br /&gt;
    {&lt;br /&gt;
        /* Pin wurde auf Masse gezogen, 100ms warten   */&lt;br /&gt;
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz&lt;br /&gt;
        _delay_ms(50); &lt;br /&gt;
        if ( *port &amp;amp; (1 &amp;lt;&amp;lt; pin) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Anwender Zeit zum Loslassen des Tasters geben */&lt;br /&gt;
            _delay_ms(50);&lt;br /&gt;
            _delay_ms(50); &lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; PB0 );                 /* PIN PB0 auf Eingang (Taster)            */&lt;br /&gt;
    PORTB |= ( 1 &amp;lt;&amp;lt; PB0 );                 /* Pullup-Widerstand aktivieren            */&lt;br /&gt;
    ...&lt;br /&gt;
    if (debounce(&amp;amp;PINB, PB0))             /* Falls Taster an PIN PB0 gedrueckt..    */&lt;br /&gt;
        PORTD = PIND ^ ( 1 &amp;lt;&amp;lt; PD7 );  /* ..LED an Port PD7 an-&lt;br /&gt;
                                   bzw. ausschalten */&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesem Beispiel ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritische Anwendungen sollte man ein anderes Verfahren nutzen (z.B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).&lt;br /&gt;
&lt;br /&gt;
Zum Thema Entprellen siehe auch:&lt;br /&gt;
* Artikel [[Entprellung]]&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls ben&amp;amp;ouml;tigt, kann eine 16-Bit Variable auch recht einfach manuell in ihre zwei 8-Bit Bestandteile zerlegt werden. Folgendes Beispiel demonstriert dies anhand des pseudo- 16-Bit Registers UBRR.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
typedef union {&lt;br /&gt;
        uint16_t i16;&lt;br /&gt;
        struct {&lt;br /&gt;
                uint8_t i8l;&lt;br /&gt;
                uint8_t i8h;&lt;br /&gt;
        };&lt;br /&gt;
} convert16to8;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
convert16to8 baud;&lt;br /&gt;
baud.i16 = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
UBRRH = baud.i8h;&lt;br /&gt;
UBRRL = baud.i8l;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/*alternativ:*/&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint16_t wFoo16;&lt;br /&gt;
uint8_t bFooLow, bFooHigh;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
wFoo16   = 0xAA55;                 /* zu &amp;quot;zerlegende&amp;quot; 16Bit-Integer */&lt;br /&gt;
bFooHigh = (uint8_t)(wFoo16 &amp;gt;&amp;gt; 8); /* MS-Byte */&lt;br /&gt;
bFooLow  = (uint8_t)(wFoo16);      /* LS-Byte */&lt;br /&gt;
...&lt;br /&gt;
#define us0(Data) ((unsigned char *)(&amp;amp;Data))&lt;br /&gt;
#define us1(Data) ((unsigned char *)((&amp;amp;Data)+1))&lt;br /&gt;
bFooHigh = us1(wFoo16);&lt;br /&gt;
bFoolow  = us0(wFoo16);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen (z.B. ATmega8) teilen sich UBRRH und UCSRC die gleiche Memory-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL) welches Register tats&amp;amp;auml;chlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und &amp;amp;uuml;bergibt den Wert &#039;&#039;3&#039;&#039; und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und &amp;amp;uuml;bergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Bei einigen 16-bit Registern (insbesondere die der 16-bit Timer) erfolgen Schreibzugriffe über das sogenannte &#039;&#039;temporary Register&#039;&#039;. Die Reihenfolge der Zugriffe bestimmt, wann der Wert tatsächlich ins Register geschrieben wird. Typisch wird erst das High-Byte beschrieben (xxxH) und intern im temporary Register zwischengespeichert. Nachdem das Low-Byte (xxxL) geschrieben wurde, setzt der Controller mit diesem und dem im temporary Register zwischengespeicherten Wert für das High-Byte das 16-bit Register. Dabei ist zu beachten, dass intern nur ein temporary Register verfügbar ist, welches in Interruptroutinen mglw. mit einem anderen Wert überschrieben wird, wenn dort ebenfalls 16-bit Register beschrieben werden. avr-gcc/avr-libc berücksichtigen die korrekte Reihenfolge automatisch, wenn die Register mit ihrem &amp;quot;16-bit Label&amp;quot; (ohne H bzw. L) angesprochen werden, dabei ist der Schutz des temporary Registers vor Überschreiben durch Interruptroutinen dennoch zu beachten (im Zweifel beim Schreibzugriff die Interrupts kurzzeitig global deaktivieren).&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t key_pressed(const volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if ( last_state == ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) ) ) {&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    i = key_pressed( &amp;amp;PINB, PB1 );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Im Übrigen können mit volatile gekennzeichnete Variablen auch als const deklariert werden, um sicherzustellen, dass sie nur noch von der Hardware änderbar sind.&lt;br /&gt;
&lt;br /&gt;
= Der UART =&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Über den [[UART]] kann ein AVR leicht mit einer [[RS-232]]-Schnittstelle eines PC oder sonstiger Geräte mit &amp;quot;serieller Schnittstelle&amp;quot; verbunden werden. &lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungen des UART:&lt;br /&gt;
&lt;br /&gt;
* Debug-Schnittstelle: z.B. zur Anzeige von Zwischenergebnissen (&amp;quot;printf-debugging&amp;quot; - hier besser &amp;quot;UART-debugging&amp;quot;) auf einem PC. Auf dem Rechner reicht dazu ein [[RS-232#Terminalprogramme|Terminalprogramm]] (MS-Windows: Hyperterm oder besser Bray-Terminal, [http://www.mikrocontroller.net/forum/read-8-155472.html#new HTerm]; Unix/Linux z.B. minicom). Ein direkter Anschluss ist aufgrund unterschiedlicher Pegel nicht möglich, jedoch sind entsprechende Schnittstellen-ICs wie z.B. ein MAX232 günstig und leicht zu integrieren. Rechner ohne serielle Schnittstelle können über fertige USB-seriell-Adapter angeschlossen werden. &lt;br /&gt;
* &amp;quot;Mensch-Maschine Schnittstelle&amp;quot;: z.B. Konfiguration und Statusabfrage über eine &amp;quot;Kommandozeile&amp;quot; oder Menüs (siehe z.B. Forumsbeitrag [http://www.mikrocontroller.net/topic/52985 Auswertung RS232-Befehle]) &lt;br /&gt;
* Übertragen von gespeicherten Werten: z.B. bei einem Datenlogger&lt;br /&gt;
* Anschluss von Geräten mit serieller Schnittstelle (z.B. (Funk-)Modems, Mobiltelefone, Drucker, Sensoren, &amp;quot;intelligente&amp;quot; LC-Displays, GPS-Empfänger). &lt;br /&gt;
* &amp;quot;Feldbusse&amp;quot; auf RS485/RS422-Basis mittels entsprechenden Bustreiberbausteinen (z.B. MAX485)&lt;br /&gt;
* DMX, Midi etc.&lt;br /&gt;
* LIN-Bus (&#039;&#039;&#039;L&#039;&#039;&#039;ocal &#039;&#039;&#039;I&#039;&#039;&#039;nterconnect &#039;&#039;&#039;N&#039;&#039;&#039;etwork): Preiswerte Sensoren/Aktoren in der Automobiltechnik und darüber hinaus&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben ein bis zwei vollduplexfähigen UART (&#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) schon eingebaut (&amp;quot;Hardware-UART&amp;quot;). &lt;br /&gt;
Übrigens: Vollduplex heißt nichts anderes, als dass der Baustein gleichzeitig senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega, ATtiny) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterscheidet sich vom UART hauptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurationsregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit zeigt an, ob der Sendepuffer bereit ist, um ein zu sendendes Zeichen aufzunehmen. Das Bit wird vom AVR gesetzt (1), wenn der Sendepuffer leer ist. Es wird gelöscht (0), wenn ein Zeichen im Sendedatenregister vorhanden ist und noch nicht in das Sendeschieberegister übernommen wurde. Atmel empfiehlt aus Kompatibilitätsgründen mit kommenden µC, UDRE auf 0 zu setzen, wenn das UCSRA Register beschrieben wird.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
\mathrm{UBRR} = \frac{\mathrm{Taktfrequenz}}{\mathrm{Baudrate} \cdot 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS-232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. &lt;br /&gt;
&amp;lt;!-- &amp;quot;Hackerloesung&amp;quot; auskommentiert - nicht so gut in einem &amp;quot;Einsteiger-Tutorial&amp;quot; - mthomas&lt;br /&gt;
Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfiehlt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== UART initialisieren ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben. Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach gewünschter Funktionsweise die benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Da wir vorerst nur senden möchten und noch keine Interrupts auswerten wollen, gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs mit USART haben mehrere Konfigurationsregister und erfordern eine etwas andere Konfiguration. Für einen ATmega16 z.B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                 // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);     // Asynchron 8N1&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist noch das Baudratenregister &#039;&#039;&#039;UBRR&#039;&#039;&#039; einzustellen. Bei neueren AVRs besteht es aus zwei Registern &#039;&#039;&#039;UBRRL&#039;&#039;&#039; und &#039;&#039;&#039;UBRRH&#039;&#039;&#039;. Der Wert dafür ergibt sich aus der angegebenen Formel durch Einsetzen der Taktfrequenz und der gewünschten Übertragungsrate. Das Berechnen der Formel wird dem [[C-Präprozessor|Präprozessor]] überlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann&lt;br /&gt;
   F_CPU im Makefile definiert werden, eine nochmalige Definition&lt;br /&gt;
   hier wuerde zu einer Compilerwarnung fuehren. Daher &amp;quot;Schutz&amp;quot; durch&lt;br /&gt;
   #ifndef/#endif &lt;br /&gt;
&lt;br /&gt;
   Dieser &amp;quot;Schutz&amp;quot; kann zu Debugsessions führen, wenn AVRStudio &lt;br /&gt;
   verwendet wird und dort eine andere, nicht zur Hardware passende &lt;br /&gt;
   Taktrate eingestellt ist: Dann wird die folgende Definition &lt;br /&gt;
   nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) &lt;br /&gt;
   von AVRStudio - daher Ausgabe einer Warnung falls F_CPU&lt;br /&gt;
   noch nicht definiert: */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000&amp;quot;&lt;br /&gt;
#define F_CPU 4000000UL    // Systemtakt in Hz &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600UL          // Baudrate&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) // Fehler in Promille, 1000 = kein Fehler.&lt;br /&gt;
&lt;br /&gt;
#if ((BAUD_ERROR&amp;lt;990) || (BAUD_ERROR&amp;gt;1010))&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;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    UBRR = UBRR_VAL;&lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&amp;lt;!-- mthomas: warum nicht UL?, wird von AVRStudio auch mit UL übergeben --&amp;gt;&lt;br /&gt;
Wieder für den Mega16 mit zwei Registern für die Baudrateneinstellung eine etwas andere Programmierung. Wichtig ist, dass UBRRH &#039;&#039;&#039;vor&#039;&#039;&#039; UBRRL geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmega16 */&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einige AVR (z.B. ATmega169, ATmega48/88/168, AT90CAN jedoch nicht für z.B. ATmega16/32, ATmega128, ATtiny2313) wird durch die Registerdefinitionen der avr-libc (io*.h) auch für Controller mit zwei UBRR-Registern (UBRRL/UBRRH) ein UBRR bzw. UBRR0 als &amp;quot;16-bit-Register&amp;quot; definiert und man kann auch Werte direkt per UBRR = UBRR_VAL zuweisen. Intern werden dann zwei Zuweisungen für UBRRH und UBRRL generiert. Dies ist nicht bei allen Controllern möglich, da die beiden Register nicht bei allen aufeinanderfolgende Addressen aufweisen. Die getrennte Zuweisung an UBRRH und UBRRL wie im Beispiel gezeigt ist universeller und portabler und daher vorzuziehen.&lt;br /&gt;
&lt;br /&gt;
Die Makros sind sehr praktisch, da sie sowohl automatisch den Wert für UBRR als auch den Fehler in der generierten Baudrate berechnen und im Falle einer Überschreitung (+/-1%) einen Fehler und somit Abbruch im Compilerablauf generieren. Damit können viele Probleme mit &amp;quot;UART sendet komische Zeichen&amp;quot; vermieden werden. Ausserdem kann man mühelos die Einstellung an eine neue Taktfrequenz bzw. Baudrate anpassen, ohne selber rechnen oder in Tabellen nachschlagen zu müssen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. Vorher ist zu prüfen, ob das UART-Modul bereit ist, das zu sendende Zeichen entgegenzunehmen. Die Bezeichnungen des/der Statusregisters mit dem Bit UDRE ist abhängig vom Controllertypen (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    // bei AVR mit einem UART (&amp;quot;classic AVR&amp;quot; z.B. AT90S8515)&lt;br /&gt;
    while (!(USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                  /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&lt;br /&gt;
    /** ODER **/&lt;br /&gt;
&lt;br /&gt;
    // bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                    /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe &amp;quot;String senden&amp;quot; wird durch zwei Funktionen abgearbeitet. Die universelle/controllerunabhängige Funktion uart_puts übergibt jeweils ein Zeichen der Zeichenkette an eine Funktion uart_putc, die abhängig von der vorhandenen Hardware implementiert werden muss. In der Funktion zum Senden eines Zeichens ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// putc fuer AVR mit einem UART (z.B. AT90S8515)&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while(!(USR &amp;amp; (1 &amp;lt;&amp;lt; UDRE)))  /* warte, bis UDR bereit */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = c;                     /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** ODER **/&lt;br /&gt;
&lt;br /&gt;
// bei neueren AVRs andere Bezeichnung fuer die Statusregister, hier ATmega16:&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich */&lt;br /&gt;
    {&lt;br /&gt;
    }                             &lt;br /&gt;
&lt;br /&gt;
    UDR = c;                      /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* puts ist unabhaengig vom Controllertyp */&lt;br /&gt;
void uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != &#039;\0&#039; also ungleich dem &amp;quot;String-Endezeichen&amp;quot; */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die in uart_putc verwendeten Schleifen, in denen gewartet wird bis die UART-Hardware zum senden bereit ist, sind insofern etwas kritisch, da während des Sendens eines Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. empfangenen Zeichen/Bytes zwischengespeichert und in Interruptroutinen an die U(S)ART-Hardware weitergegeben bzw. von ihr ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&lt;br /&gt;
&lt;br /&gt;
=== Schreiben von Variableninhalten ===&lt;br /&gt;
&lt;br /&gt;
Sollen Inhalte von Variablen (Ganzzahlen, Fließkomma) in &amp;quot;menschenlesbarer&amp;quot; Form gesendet werden, ist vor dem Transfer eine Umwandlung in Zeichen (&amp;quot;ASCII&amp;quot;) erforderlich. Bei nur einer Ziffer ist diese Umwandlung relativ einfach: man addiert den ASCII-Wert von Null zur Ziffer und kann diesen Wert direkt senden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_putc (s.o.)&lt;br /&gt;
&lt;br /&gt;
// Ausgabe von 0123456789&lt;br /&gt;
char c;&lt;br /&gt;
&lt;br /&gt;
for (uint8_t i=0; i&amp;lt;=9; ++i) {&lt;br /&gt;
    c = i + &#039;0&#039;;&lt;br /&gt;
    uart_putc( c );&lt;br /&gt;
    // verkuerzt: uart_putc( i + &#039;0&#039; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll mehr als eine Ziffer ausgegeben werden, bedient man sich zweckmäßigerweise vorhandener Funktionen zur Umwandlung von Zahlen in Zeichenketten/Strings. Die Funktion der avr-libc zur Umwandlung von vorzeichenbehafteten 16bit-Ganzzahlen (int16_t) in Zeichenketten heißt &#039;&#039;itoa&#039;&#039; (Integer to ASCII). Man muss der Funktion einen Speicherbereich zur Verarbeitung (buffer) mit Platz für alle Ziffern, das String-Endezeichen (&#039;\0&#039;) und evtl. das Vorzeichen bereitstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   char s[7];&lt;br /&gt;
   int16_t i = -12345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   itoa( i, s, 10 ); // 10 fuer radix -&amp;gt; Dezimalsystem&lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // da itoa einen Zeiger auf den Beginn von s zurueckgibt verkuerzt auch:&lt;br /&gt;
   uart_puts( itoa( i, s, 10 ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für vorzeichenlose 16bit-Ganzzahlen (uint16_t) exisitert &#039;&#039;utoa&#039;&#039;. Die Funktionen für 32bit-Ganzzahlen (int32_t und uint32_t) heißen &#039;&#039;ltoa&#039;&#039; bzw. &#039;&#039;ultoa&#039;&#039;. Da 32bit-Ganzzahlen mehr Stellen aufweisen können, ist ein entsprechend größerer Pufferspeicher vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Auch Fließkommazahlen (float/double) können mit breits vorhandenen Funktionen in Zeichenfolgen umgewandelt werden, dazu existieren die Funktionen &#039;&#039;dtostre&#039;&#039; und &#039;&#039;dtostrf&#039;&#039;. dtostre nutzt Exponentialschreibweise (&amp;quot;engineering&amp;quot;-Format). (Hinweis: z.Zt. existiert im avr-gcc kein &amp;quot;echtes&amp;quot; double, intern wird immer mit &amp;quot;einfacher Genauigkeit&amp;quot;, entsprechend float, gerechnet.) &lt;br /&gt;
&lt;br /&gt;
dtostrf und dtostre benötigen die libm.a der avr-libc. Bei Nutzung von Makefiles ist der Parameter -lm in in LDFLAGS anzugeben (Standard in den WinAVR/mfile-Makefilevorlagen). Nutzt man AVRStudio als IDE für den GNU-Compiler (gcc-Plugin) ist die libm.a unter Libaries auszuwählen: Project -&amp;gt; Configurations Options -&amp;gt; Libaries -&amp;gt; libm.a mit dem Pfeil nach rechts einbinden. Siehe auch die [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|FAQ]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
/* lt. avr-libc Dokumentation:&lt;br /&gt;
char* dtostrf(&lt;br /&gt;
  double __val,&lt;br /&gt;
  char   __width,&lt;br /&gt;
  char   __prec,&lt;br /&gt;
  char * __s&lt;br /&gt;
)  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   // Pufferspeicher ausreichend groß&lt;br /&gt;
   // evtl. Vorzeichen + width + Endezeichen:&lt;br /&gt;
   char s[8]; &lt;br /&gt;
   float f = -12.345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   dtostrf( f, 6, 3, s ); &lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // verkürzt: uart_puts( dtostrf( f, 7, 3, s ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfangen ==&lt;br /&gt;
=== einzelne Zeichen empfangen ===&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von Zeichen muss der Empfangsteil des UART bei der Initialisierung aktiviert werden, indem das RXEN-Bit im jeweiligen Konfigurationsregister (UCSRB bzw UCSR0B/UCSR1B) gesetzt wird. Im einfachsten Fall wird solange gewartet, bis ein Zeichen empfangen wurde, dieses steht dann im UART-Datenregister (UDR bzw. UDR0 und UDR1 bei AVRs mit 2 UARTS) zur Verfügung (sogen. &amp;quot;Polling-Betrieb&amp;quot;). Ein Beispiel für den ATmega16:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Zusaetzlich zur Baudrateneinstellung und der weiteren Initialisierung: */&lt;br /&gt;
void Usart_EnableRX()&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= ( 1 &amp;lt;&amp;lt; RXEN );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion blockiert den Programmablauf. Alternativ kann das RXC-Bit in einer Programmschleife abgefragt werden und dann nur bei gesetztem RXC-Bit UDR ausgelesen werden. Eleganter und in den meisten Anwendungsfällen &amp;quot;stabiler&amp;quot; ist die Vorgehensweise, die empfangenen Zeichen in einer Interrupt-Routine einzulesen und zur späteren Verarbeitung in einem Eingangsbuffer (FIFO-Buffer) zwischenzuspeichern. Dazu existieren fertige und gut getestete [[Libraries|Bibliotheken]] &amp;lt;!-- &amp;quot;echte Libraries&amp;quot; (.a) wie im Verweis beschrieben sind hier eigentlich nicht gemeint, verwirrt hier etwas, da AVR-&amp;quot;Libraries&amp;quot; meist per #defines anpassbare Source-Codes sind, vielleicht so: --&amp;gt; und Quellcodekomponenten (z.B. UART-Library von P. Fleury, procyon-avrlib und einige in der &amp;quot;Academy&amp;quot; von avrfreaks.net).&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html Dokumenation der avr-libc/stdlib.h]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_printf Die Nutzung von printf]&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&amp;lt;!-- nimmermehr: * siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (printf etc.) im [[AVR-Tutorial:_UART]]. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: 9bit&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Zeichenketten (Strings) ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Zeichenketten, muß man sich zunächst darüber im klaren sein, daß es ein Kriterium geben muß, an dem der µC erkennen kann, wann ein String zu Ende ist. Sehr oft wird dazu das Zeichen &#039;Return&#039; benutzt, um das Ende eines Strings zu markieren. Dies ist vom Benutzer einfach eingebbar und er ist auch daran gewöhnt, daß er eine Eingabezeile mit einem Druck auf die Return Taste abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Prinzipiell gibt es jedoch keine Einschränkung bezüglich dieses speziellen Zeichens. Es muß nur sichergestellt werden, daß dieses spezielle &#039;Ende eines Strings&#039; - Zeichen nicht mit einem im Text vorkommenden Zeichen verwechselt werden kann. Wenn also im zu übertragenden Text beispielsweise kein &#039;;&#039; vorkommt, dann spricht nichts dagegen, einen String mit einem &#039;;&#039; abschließen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird die durchaus übliche Annahme getroffen, daß eine Stringübertragung identisch ist mit der Übertragung einer Textzeile und daher mit einem Return (&#039;\n&#039;) abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Das Problem der Übertragung eines Strings reduziert sich damit auf die Aufgabenstellung: Empfange und Sammle Zeichen in einem char Array, bis entweder das Array voll ist oder das &#039;String Ende Zeichen&#039; empfangen wurde. Danach wird der empfangene Text noch mit einem &#039;\0&#039; Zeichen abgeschlossen um einen Standard C-String daraus zu machen, mit dem dann weiter gearbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_gets( char* Buffer, uint8_t MaxLen )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t NextChar;&lt;br /&gt;
  uint8_t StringLen = 0;&lt;br /&gt;
&lt;br /&gt;
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen&lt;br /&gt;
&lt;br /&gt;
                                  // Sammle solange Zeichen, bis:&lt;br /&gt;
                                  // * entweder das String Ende Zeichen kam&lt;br /&gt;
                                  // * oder das aufnehmende Array voll ist&lt;br /&gt;
  while( NextChar != &#039;\n&#039; &amp;amp;&amp;amp; StringLen &amp;lt; MaxLen - 1 ) {&lt;br /&gt;
    *Buffer++ = NextChar;&lt;br /&gt;
    StringLen++;&lt;br /&gt;
    NextChar = uart_getc();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
                                  // Noch ein &#039;\0&#039; anhängen um einen Standard&lt;br /&gt;
                                  // C-String daraus zu machen&lt;br /&gt;
  *Buffer = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf ist darauf zu achten, dass das empfangende Array auch mit einer&lt;br /&gt;
vernünftigen Größe definiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
  char Line[40];      // String mit maximal 39 zeichen&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) );&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Interruptbetrieb==&lt;br /&gt;
&lt;br /&gt;
Beim ATMEGA8 ist es möglich, dass RXCIE Bit im Register UCSRB zu setzen.&lt;br /&gt;
Dieser Interrupt wird immer ausglöst, wenn Daten erfolgreich empfangen wurden.&lt;br /&gt;
Zusätzlich braucht man die Routine:&lt;br /&gt;
&lt;br /&gt;
ISR (USART_RXC_vect) {&lt;br /&gt;
   //irgendein Code&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
natürlich muss &amp;quot;Global Interrupt Enable&amp;quot; Aktiviert sein.&lt;br /&gt;
!! Nur getestet beim ATMEGA8 !!&lt;br /&gt;
&lt;br /&gt;
[BAUSTELLE! Aus Lerngründen eventuell als eigenen UART-Interrupt-Block hinter den grundlegenden Interrupt-Teil im Tutorial und hier eine kurze Einführung und einen Verweis darauf anbieten.]&lt;br /&gt;
&lt;br /&gt;
* Unterschied Polling-Betrieb (bisher, oben) und Interrupt-Betrieb&lt;br /&gt;
* Empfangen (Receive)&lt;br /&gt;
** Verändertes UART-Init, ISR (RXC), ggf. Fallstricke ([http://www.mikrocontroller.net/topic/84256#707214 UDR in der ISR lesen!]), Philosophie einer ISR (kurz und schmerzlos), Datenaustausch ISR zu Restprogramm (volatile)&lt;br /&gt;
** Einfachstbeispiel ([http://www.mikrocontroller.net/topic/84228#707052 Echo] (noch buggy beim Datenzugriff, siehe Lit. 2+3!)), ggf. LED zur ISR-Empfangsanzeige oder Overflow-Anzeige&lt;br /&gt;
* FIFO-Puffer, Ringpuffer&lt;br /&gt;
* Senden (Transmit)&lt;br /&gt;
** Variante &amp;quot;UART Data Register Empty&amp;quot; (UDRE) &lt;br /&gt;
** Variante &amp;quot;UART Transmit Complete&amp;quot; (TXC) &lt;br /&gt;
* Fertige UART-Bibliotheken (-&amp;gt;Fleury, -&amp;gt;Procyon)&lt;br /&gt;
* Literatur: &lt;br /&gt;
** 1/ [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=48188 avrfreaks.net Tutorial] inkl. Diskussion (engl.)&lt;br /&gt;
** 2/ avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_16bitio Why do some 16-bit timer registers sometimes get trashed?]&lt;br /&gt;
** 3/ [[Interrupt]] und atomarer Datenzugriff&lt;br /&gt;
&lt;br /&gt;
==Software-UART==&lt;br /&gt;
&lt;br /&gt;
Falls die Zahl der vorhandenen Hardware-UARTs nicht ausreicht, können weitere Schnittstellen über sogennante Software-UARTs ergänzt werden. Es gibt dazu (mindestens) zwei Ansätze: &lt;br /&gt;
* Der bei AVRs üblichste Ansatz basiert auf dem Prinzip, dass ein externer Interrupt-Pin für den Empfang (&amp;quot;RX&amp;quot;) genutzt wird. Das Startbit löst den Interrupt aus, in der Interrupt-Routine (ISR) wird der externe Interrupt deaktiviert und ein Timer aktiviert. In der Interrupt-Routine des Timers wird der Zustand des Empfangs-Pins entsprechend der Baudrate abgetastet. Nach Empfang des Stop-Bits wird der externe Interrupt wieder aktiviert. Senden kann über einen beliebigen Pin (&amp;quot;TX&amp;quot;) erfolgen, der entsprechend der Baudrate und dem zu sendenden Zeichen auf 0 oder 1 gesetzt wird. Die Implementierung ist nicht ganz einfach, es existieren dazu aber fertige Bibliotheken (z.B. bei [http://www.avrfreaks.net/ avrfreaks] oder in der [http://hubbard.engr.scu.edu/embedded/avr/avrlib/ Procyon avrlib]).&lt;br /&gt;
* Ein weiterer Ansatz erfordert keinen Pin mit &amp;quot;Interrupt-Funktion&amp;quot; aber benötigt mehr Rechenzeit. Jeder Input-Pin kann als Empfangspin (RX) dienen. Über einen Timer wird der Zustand des RX-Pins mit einem vielfachen der Baudrate abgetastet (dreifach scheint üblich) und High- bzw. Lowbits anhand einer Mindestanzahl identifiziert. (Beispiel: &amp;quot;Generic Software Uart&amp;quot; Application-Note von IAR)&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (z.B. ATtiny26 oder ATmega48,88,168,169) verfügen über ein Universal Serial Interface (USI), das teilweise UART-Funktion übernehmen kann. Atmel stellt eine Application-Note bereit, in der die Nutzung des USI als UART erläutert wird (im Prinzip &amp;quot;Hardware-unterstützter Software-UART&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==Handshaking==&lt;br /&gt;
Wenn der Sender ständig sendet, wird irgendwann der Fall eintreten, daß der Empfänger nicht bereit ist, neue Zeichen zu empfangen. In diesem Fall muß durch ein &#039;&#039;&#039;Handshake-Verfahren&#039;&#039;&#039; die Situation bereinigt werden. Handshake bedeutet nichts anderes, als daß der Empfänger dem Sender mitteilt, daß er zur Zeit keine Daten annehmen kann und der Sender die Übertragung der nächsten Zeichen solange einstellen soll, bis der Empfänger signalisiert, daß er wieder Zeichen aufnehmen kann.&lt;br /&gt;
===Hardwarehandshake (RTS/CTS)===&lt;br /&gt;
Beim Hardwarehandshake werden zusätzlich zu den beiden Daten-Übertragungsleitungen noch 2 weitere Leitungen benötigt: &#039;&#039;&#039;RTS&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;equest &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end) und &#039;&#039;&#039;CTS&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end). Jeder der beiden Kommunikationspartner ist verpflichtet, bevor ein Zeichen gesendet wird, den Zustand der &#039;&#039;&#039;RTS&#039;&#039;&#039; Leitung zu überprüfen. Nur wenn die Gegenstelle darauf Empfangsbereitschaft signalisiert, darf das Zeichen gesendet werden. Um der Gegenstelle zu signalisieren, daß sie zur Zeit keine Zeichen schicken soll, wird die Leitung &#039;&#039;&#039;CTS&#039;&#039;&#039; benutzt.&lt;br /&gt;
&lt;br /&gt;
===Softwarehandshake (XON/XOFF)===&lt;br /&gt;
Beim Softwarehandshake sind keine speziellen Leitungen notwendig. Statt dessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, daß Senden einzustellen bzw. wieder aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;XOFF&#039;&#039;&#039; Aufforderung das Senden einzustellen&lt;br /&gt;
* &#039;&#039;&#039;XON&#039;&#039;&#039;  Gegenstelle darf wieder senden&lt;br /&gt;
&lt;br /&gt;
Nachteilig bei einem Softwarehandshake ist es, daß dadurch keine direkte binäre Datenübertragung mehr möglich ist. Von den möglichen 256 Bytewerten werden ja 2 (nämlich &#039;&#039;&#039;XON&#039;&#039;&#039; und &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus.&lt;br /&gt;
&lt;br /&gt;
==Fehlersuche==&lt;br /&gt;
Erstaunlich oft wird im Forum der Hilferuf laut: &amp;quot;Meine UART funktioniert nicht, was mache ich falsch&amp;quot;. In der überwiegenden Mehrzahl der Fälle stellt sich dann heraus, daß es sich um ein Hardwareproblem handelt, wobei da wiederrum der Löwenanteil auf das Konto einer nicht korrekt eingestellten Taktrate geht: Der µC benutzt nicht einen angeschlossenen Quarz, so wie er auch im Programm eingetragen ist, sondern läuft immer noch mit dem internen RC-Takt. Daraus resultiert aber auch, daß der Baudraten Konfigurationswert falsch berechnet wird.&lt;br /&gt;
&lt;br /&gt;
Eine Checkliste zum Aufspüren solcher Fehler findet sich [[AVR_Checkliste#UART.2FUSART|hier]].&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Analoge Eingangswerte werden in der Regel über den AVR Analog-Digital-Converter (AD-Wandler, ADC) eingelesen, der in vielen Typen verfügbar ist (typisch 10bit Auflösung). Durch diesen werden analogen Signale (Spannungen) in digitale Zahlenwerte gewandelt. Bei AVRs, die über keinen internen AD-Wandler verfügen (z.B. ATmega162, ATtiny2313), kann durch externe Beschaltung (R/C-Netzwerk und &amp;quot;Zeitmessung&amp;quot;) die Funktion des AD-Wandlers &amp;quot;emuliert&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
Es existieren keine AVRs mit eingebautem Digital-Analog-Konverter (DAC). Diese Funktion muss durch externe Komponenten nachgebildet werden (z.B. PWM und &amp;quot;Glättung&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Unabhängig davon besteht natürlich immer die Möglichkeit, spezielle Bausteine zur Analog-Digital- bzw. Digital-Analog-Wandlung zu nutzen und diese über eine digitale Schnittstelle (z.b. SPI oder I2C) mit einem AVR anzusteuern.&lt;br /&gt;
&lt;br /&gt;
== AC (Analog Comparator) ==&lt;br /&gt;
&lt;br /&gt;
Der Comparator vergleicht 2 Spannungen an den Pins AIN0 und AIN1 und gibt einen Status aus welche der beiden Spannungen größer ist. AIN0 Dient dabei als Referenzspannung (Sollwert) und AIN1 als Vergleichsspannung (Istwert). Als Referenzspannung kann auch alternativ eine interne Referenzspannung ausgewählt werden.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Liegt die Vergleichsspannung (IST) unter der der Referenzspannung (SOLL) gibt der Comperator eine logische 0 aus. ISt die Vergleichsspannung hingegen größer als die Referenzspannung wird eine logische 1 ausgegeben.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Der Comperator arbeitet völlig autark bzw. parallel zum Prozessor. Für mobile Anwendungen empfiehlt es sich ihn abzuschalten sofern er nicht benötigt wird, da er ansonsten Strom benötigt. Der Comparator kann Interruptgesteuert abgefragt werden oder im Pollingbetrieb.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Steuer- bzw. Statusregister ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ACSR (0x28) - Analog Comparator Status Register&amp;lt;BR&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACD&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;AINBG&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACO&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACI&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | n/a&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 7 ACD - Analog Comperator Disable: 0 = Comparator ein, 1 = Comparator aus. Wird dieses Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 ACIE ggf. abgeschaltet werden.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 6 AINBG - Analog Comparator Bandgap Select: Ermöglicht das umschalten zwischen interner und externer Referenzspannung. 1 = interne (~1,3 Volt), 0 = externe Referenzspannung (an Pin AIN0)&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 5 ACO - Analog Coparator Output: Hier wird das Ergebnis des Vergleichs angezeigt. Es liegt typischerweise nach 1-2 Taktzyklen vor. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ergebnis: &amp;lt;BR&amp;gt;IST &amp;lt; SOLL --&amp;gt; 0&amp;lt;BR&amp;gt;&lt;br /&gt;
IST &amp;gt; SOLL --&amp;gt; 1&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4 ACI - Analog Comparator Interrupt Flag: Dieses Bit wird von der Hardware gesetzt wenn ein Interruptereignis das in Bit 0 und 1 definiert ist eintritt. Dieses Bit löst noch keinen Interrupt aus! Die Interruptroutine wird nur dann ausgeführt wenn das Bit 3 ACIE gesetzt ist und global Interrupts erlaubt sind (I-Bit in SREG=1). Das Bit 4 ACI wird wieder gelöscht wenn die Interruptroutine ausgeführt wurde oder wenn manuell das Bit auf 1 gesetzt wird. Das Bit kann für Abfragen genutzt werden, steuert oder konfuguriert aber nicht den Comparator.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 3 ACIE - Analog Comparator Interrupt Enable: Ist das Bit auf 1 gesetzt wird immer ein Interrupt ausgelöst wenn das Ereignis das in Bit 1 und 0 definiert ist eintritt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 2 ACIC - Analog Comparator Input Capture Enable: Wird das Bit gesetzt wird der Comparatorausgang intern mit dem Counter 1 verbunden. Es könnten damit z.b. die Anzahl der Vergleiche im Counter1 gezählt werden. Um den Comparator an den Timer1 Input Capture Interrupt zu verbinden muß im Timerregister das TICIE1 Bit auf 1 gesetzt werden. Der Trigger wird immer dann ausgelöst wenn das in Bit 1 und 0 definierte Ereignis eintritt.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 1,0 ACIS1,ACIS0 - Analog Comparator Interrupt select: Hier wird definiert welche Ereignisse einen Interrupt auslösen sollen:&amp;lt;BR&amp;gt;&lt;br /&gt;
00 = Interrupt auslösen bei jedem Flankenwechsel&amp;lt;BR&amp;gt;&lt;br /&gt;
10 = Interrupt auslösen bei fallender Flanke&amp;lt;BR&amp;gt;&lt;br /&gt;
11 = Interrupt auslösen bei steigender Flanke&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Werden diese Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 gelöscht werden.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der Analog-Digital-Konverter (ADC) wandelt analoge Signale in digitale Werte um, welche vom Controller interpretiert werden können. Einige AVR-Typen haben bereits einen mehrkanaligen Analog-Digital-Konverter eingebaut. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst werden kann, wird durch die Auflösung des ADC in Anzahl Bits angegeben, man hört bzw. liest jeweils von 8-Bit-ADC oder 10-Bit-ADC oder noch höher. ADCs die in AVRs enthalten sind haben zur Zeit eine maximale Auflösung von 10-Bit.&lt;br /&gt;
&lt;br /&gt;
Ein ADC mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des Analog-Digital-Konverters ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Der interne ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem Analog-Digital-Wandler (ADC) zurückgreifen, die über mehrere Kanäle verfügen. Kanäle heißt in diesem Zusammenhang, dass zwar bis zu zehn analoge Eingänge am AVR verfügbar sind, aber nur ein &amp;quot;echter&amp;quot; Analog-Digital-Wandler zur Verfügung steht, vor der eigentlichen Messung ist also einzustellen, welcher Kanal (&amp;quot;Pin&amp;quot;) mit dem Wandler verbunden und gemessen wird.&lt;br /&gt;
&lt;br /&gt;
Die Umwandlung innerhalb des AVR basiert auf der schrittweisen Näherung. Beim AVR müssen die Pins &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AVCC&#039;&#039;&#039; beschaltet werden. Für genaue Messungen sollte AVCC über ein L-C Netzwerk mit VCC verbunden werden, um Spannungsspitzen und -einbrüche vom Analog-Digital-Wandler fernzuhalten. Im Datenblatt findet sich dazu eine Schaltung, die 10uH und 100nF vorsieht.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Analog-Digital-Wandlung wird auf eine Referenzspannung bezogen. Aktuelle AVRs bieten drei Möglichkeiten zur Wahl dieser Spannung:&lt;br /&gt;
&lt;br /&gt;
* Eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; am Anschlusspin &#039;&#039;&#039;AREF&#039;&#039;&#039;. Die minimale (externe) Referenzspannung darf jedoch nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. &lt;br /&gt;
&lt;br /&gt;
* Verfügt der AVR über eine interne Referenzspannung, kann diese genutzt werden. Alle aktuellen AVRs mit internem AD-Wandler sollten damit ausgestattet sein (vgl. Datenblatt: 2,56V oder 1,1V je nach Typ). Das Datenblatt gibt auch über die Genauigkeit dieser Spannung Auskunft.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;interne&#039;&#039;&#039; Referenzspannung wird auf &#039;&#039;&#039;Vcc&#039;&#039;&#039; bezogen, eine externe Referenzspannung auf &#039;&#039;&#039;GND (Masse)&#039;&#039;&#039;. Davon unabhängig werden digitalisierte Spannungen &#039;&#039;&#039;immer&#039;&#039;&#039; auf GND bezogen. Beim ATMEGA8 z.B. ist der Pin AREF über 32kOhm mit GND verbunden, d.h. man muss diese doch extrem niedrige Eingangsimpedanz mit in die Berechnung für einen Spannungsteiler einbeziehen, bzw. kann diesen Widerstand als R2 gleich mit benutzen. Formel für Spannungsteiler: Udiv = U / ((R1 + R2) / R2)&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung von Vcc oder der internen Referenz wird empfohlen, einen Kondensator zwischen dem AREF-Pin und GND anzuordnen. Die Festlegung, welche Spannungsreferenz genutzt wird, erfolgt z.B. beim ATmega16 mit den Bits REFS1/REFS0 im ADMUX-Register. Die zu messende Spannung muss im Bereich zwischen &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AREF&#039;&#039;&#039; (egal ob intern oder extern) liegen. &lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; kann in zwei verschiedenen Betriebsarten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
; Einfache Wandlung (Single Conversion) : In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestoßen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
; Frei laufend (Free Running) : In dieser Betriebsart erfasst der Wandler permanent die anliegende Spannung und schreibt diese in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Die Register des ADC ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; verfügt über eigene Register. Im Folgenden die Registerbeschreibung eines  ATMega16, welcher über 8 ADC-Kanäle verfügt. Die Register unterscheiden sich jedoch nicht erheblich von denen anderer AVRs (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister A.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den &#039;&#039;&#039;ADC&#039;&#039;&#039; verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADEN&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able)&lt;br /&gt;
:Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren. Wenn das Bit nicht gesetzt ist, können die Pins wie normale I/O-Pins verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADSC&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion)&lt;br /&gt;
:Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.&lt;br /&gt;
:Wenn das Bit nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.&lt;br /&gt;
:Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADFR&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;F&#039;&#039;&#039;ree &#039;&#039;&#039;R&#039;&#039;&#039;un select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Ist das Bit auf 1 gesetzt arbeitet der ADC im Freerunning Modus. Dabe iwird das Datenregister permanent aktualisiert. Ist das Bit hingegen auf 0 gesetzt macht der ADC nur eine Single Conversion. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIF&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag)&lt;br /&gt;
:Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt, sobald eine Umwandlung erfolgt ist und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert wurde. Das Bit wird bei lesendem Zugriff auf &#039;&#039;&#039;ADC(L,H)&#039;&#039;&#039; automatisch (d.h. durch die Hardware) gelöscht.&lt;br /&gt;
:Wenn das &#039;&#039;&#039;ADIE&#039;&#039;&#039; Bit sowie das &#039;&#039;&#039;I-Bit&#039;&#039;&#039; im AVR &#039;&#039;&#039;Statusregister&#039;&#039;&#039; gesetzt ist, wird der &#039;&#039;&#039;ADC Interrupt&#039;&#039;&#039; ausgelöst und die Interrupt-Behandlungsroutine aufgerufen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn die Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 in das Register geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIE&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;, dann wird der &#039;&#039;&#039; ADC-Interrupt&#039;&#039;&#039; aktiviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADPS2...ADPS0&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;P&#039;&#039;&#039;rescaler &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
:Der &#039;&#039;&#039;ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz ergibt.&lt;br /&gt;
:Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
TF_{min}=\frac{CLK}{200\,\mathrm{kHz}}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50\,\mathrm{kHz}}=\frac{4000000}{50000}=\mathbf{80}&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im Interesse der schnelleren Wandlungszeit werden wir hier den Faktor 32 einstellen.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCL&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ADCH&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC &#039;&#039;&#039; Data Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn eine Umwandlung abgeschlossen ist, befindet sich der gemessene Wert in&lt;br /&gt;
diesen beiden Registern. Von &#039;&#039;&#039;ADCH&#039;&#039;&#039; werden nur die beiden niederwertigsten Bits verwendet. Es müssen immer beide Register ausgelesen werden, und zwar immer &#039;&#039;&#039;in der Reihenfolge: ADCL, ADCH&#039;&#039;&#039;. &lt;br /&gt;
Der effektive Messwert ergibt sich dann zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCL;       // mit uint16_t x&lt;br /&gt;
x += (ADCH&amp;lt;&amp;lt;8); // in zwei Zeilen (LSB/MSB-Reihenfolge und&lt;br /&gt;
                // C-Operatorpriorität sichergestellt)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADMUX&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Mu&#039;&#039;&#039;ltiple&#039;&#039;&#039;x&#039;&#039;&#039;er Select Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Register wird der zu messende Kanal ausgewählt. Beim 90S8535&lt;br /&gt;
kann jeder Pin von Port A als &#039;&#039;&#039;ADC&#039;&#039;&#039;-Eingang verwendet werden (=8 Kanäle).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADLAR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX4&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX3&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;REFS1...REFS0&#039;&#039;&#039; (&#039;&#039;&#039;Ref&#039;&#039;&#039;erence&#039;&#039;&#039;S&#039;&#039;&#039;election Bits)&lt;br /&gt;
:Mit diesen Bits kann die Referenzspannung eingestellt werden:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Referenzspanung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Externes AREF&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | AVCC als Referenz&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Interne 2,56 Volt&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADLAR&#039;&#039;&#039; (&#039;&#039;&#039;ADC &#039;&#039;&#039; &#039;&#039;&#039;L&#039;&#039;&#039;eft &#039;&#039;&#039;A&#039;&#039;&#039;djust &#039;&#039;&#039;R&#039;&#039;&#039;esult)&lt;br /&gt;
:Das ADLAR Bit verändert das Aussehen des Ergebnisses der AD-Wandlung. Bei einer logischen 1 wird das Ergebnis linksbündig ausgegeben, bei einer 0 rechtsündig. Eine Änderung in diesem Bit beeinflusst das Ergebnis sofort, ganz egal ob bereits eine Wandlung läuft.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX4...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 5 Bits wird der zu messende Kanal bestimmt. Wenn man einen einfachen 1-kanaligen ADC verwendet wird einfach die entsprechende Pinnummer des Ports in die Bits 0...2 eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
:Eine Empfehlung ist deswegen diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren des ADC ====&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039; ADC&#039;&#039;&#039; zu aktivieren, müssen wir das &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bit im &#039;&#039;&#039;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
Ein kleines Beispiel für den &amp;quot;single conversion&amp;quot;-Mode bei einem ATmega169 und Nutzung der internen Referenzspannung (beim &#039;169 1,1V bei anderen AVRs auch 2,56V). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler einstellen. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1023 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint16_t ReadChannel(uint8_t mux)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
&lt;br /&gt;
  ADMUX = mux;                      // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler &lt;br /&gt;
                               // setzen auf 8 (1) und ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  /* nach Aktivieren des ADC wird ein &amp;quot;Dummy-Readout&amp;quot; empfohlen, man liest&lt;br /&gt;
     also einen Wert und verwirft diesen, um den ADC &amp;quot;warmlaufen zu lassen&amp;quot; */&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
     ;     // auf Abschluss der Konvertierung warten &lt;br /&gt;
  }&lt;br /&gt;
  result = ADCW;  // ADCW muss einmal gelesen werden,&lt;br /&gt;
                  // sonst wird Ergebnis der nächsten Wandlung&lt;br /&gt;
                  // nicht übernommen.&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  result = 0; &lt;br /&gt;
  for( i=0; i&amp;lt;4; i++ )&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);            // eine Wandlung &amp;quot;single conversion&amp;quot;&lt;br /&gt;
    while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
      ;   // auf Abschluss der Konvertierung warten&lt;br /&gt;
    }&lt;br /&gt;
    result += ADCW;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Beispielaufrufe: */&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
&lt;br /&gt;
  adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -&amp;gt; Channel 0 */&lt;br /&gt;
  ...&lt;br /&gt;
  adcval = ReadChannel(2); /* MUX-Bits auf 0b0010 -&amp;gt; Channel 2 */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekennzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Das Löschen des ADIF-Flags sollte, &#039;&#039;&#039;entgegen&#039;&#039;&#039; der [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_intbits FAQ], mit&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADIF);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
erfolgen. Die Methode in der FAQ eignet sich nur für Register in denen &#039;&#039;&#039;nur&#039;&#039;&#039; Interrupt-Flags stehen.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Analog-Digital-Wandlung ohne internen ADC ===&lt;br /&gt;
&lt;br /&gt;
==== Messen eines Widerstandes ====&lt;br /&gt;
&lt;br /&gt;
Analoge Werte lassen sich ohne Analog-Digital-Wandler auch indirekt ermitteln. Im Folgenden wird die Messung des an einem Potentiometer eingestellten Widerstands anhand der Ladekurve eines Kondensators erläutert. Bei dieser Methode wird nur ein Portpin benötigt, ein Analog-Digital-Wandler oder Analog-Comparator ist nicht erforderlich. Es wird dazu ein Kondensator und der Widerstand (das Potentiometer) in Reihe zwischen Vorsorgungsspannung und Masse/GND geschaltet (sogen. RC-Netzwerk). Zusätzlich wird eine Verbindung der Leitung zwischen Kondensator und Potentiometer zu einem Portpin des Controllers hergestellt. Die folgende Abbildung verdeutlicht die erforderliche Schaltung. &lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wird der Portpin des Controllers auf Ausgang konfiguriert (im Beispiel &#039;&#039;DDRD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) und dieser Ausgang auf Logisch 1 (&amp;quot;High&amp;quot;, &#039;&#039;PORTD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) geschaltet, liegt an beiden &amp;quot;Platten&amp;quot; des Kondensators das gleiche Potential &#039;&#039;&#039;VCC&#039;&#039;&#039; an und der Kondensator somit entladen. (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so, da an beiden Seiten des Kondensators das gleiche Potential anliegt und somit eine Potentialdifferenz von 0V besteht =&amp;gt; Kondensator ist entladen).&lt;br /&gt;
&lt;br /&gt;
Nach einer gewissen Zeit ist der Kondensator entladen und der Portpin wird als Eingang konfiguriert (&#039;&#039;DDRD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2); PORTD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2)&#039;&#039;), wodurch dieser hochohmig wird. Der Status des Eingangspin (in PIND) ist Logisch 1 (High). Der Kondensator lädt sich jetzt über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), wird das Eingangssignal als LOW erkannt (Bit in PIND wird 0). Die Zeitspanne zwischen der Umschaltung von Entladung auf Aufladung und dem Wechsel des Eingangssignals von High auf Low ist ein Maß für den am Potentiometer eingestellten Widerstand. Zur Zeitmessung kann einer der im Controller vorhandenen Timer genutzt werden. Der 220 Ohm Widerstand dient dem Schutz des Controllers. Es würde sonst bei Maximaleinstellung des Potentionmeters (hier 0 Ohm) ein zu hoher Strom fließen, der die Ausgangsstufe des Controllers zerstört. &lt;br /&gt;
&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B: 0...100 % oder so) umzurechnen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Link 404 =&amp;gt; auskommentiert, mthomas 9.2.2008 &lt;br /&gt;
Ein Beispielprogramm findet sich auf [http://www.mypage.bluewin.ch/ch_schifferle/ Christian Schifferles Web-Seite] im Archiv &#039;&#039;ATMEL.ZIP&#039;&#039;, welches unter den Titel &#039;&#039;Tutorial &amp;quot;Programmieren mit C für Atmel Mikrocontroller&#039;&#039; heruntergeladen werden kann. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ADC über Komparator ====&lt;br /&gt;
&lt;br /&gt;
Es gibt einen weiteren Weg, eine analoge Spannung mit Hilfe des&lt;br /&gt;
Komparators, welcher in fast jedem AVR integriert ist, zu messen. Siehe dazu&lt;br /&gt;
auch die Application Note AVR400 von Atmel.&lt;br /&gt;
&lt;br /&gt;
Dabei wird das zu messende Signal auf den invertierenden Eingang&lt;br /&gt;
des Komparators geführt. Zusätzlich wird ein Referenzsignal an den nicht&lt;br /&gt;
invertierenden Eingang des Komparators angeschlossen. Das Referenzsignal wird&lt;br /&gt;
hier auch wieder über ein RC-Glied erzeugt, allerdings mit festen Werten für R&lt;br /&gt;
und C.&lt;br /&gt;
&lt;br /&gt;
[[Image:ADC ueber Komparator.gif]]&lt;br /&gt;
&lt;br /&gt;
Das Prinzip der Messung ist nun dem vorhergehenden recht&lt;br /&gt;
ähnlich. Durch Anlegen eines LOW-Pegels an Pin 2 wird der Kondensator zuerst&lt;br /&gt;
einmal entladen. Auch hier muss darauf geachtet werden, dass der Entladevorgang&lt;br /&gt;
genügend lang dauert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Nun wird Pin 2 auf HIGH gelegt. Der Kondensator wird geladen. Wenn die Spannung&lt;br /&gt;
über dem Kondensator die am Eingangspin anliegende Spannung erreicht hat&lt;br /&gt;
schaltet der Komparator durch. Die Zeit, welche benötigt wird, um den&lt;br /&gt;
Kondensator zu laden kann nun auch wieder als Maß für die Spannung an Pin 1&lt;br /&gt;
herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Ich habe es mir gespart, diese Schaltung auch aufzubauen und&lt;br /&gt;
zwar aus mehreren Gründen:&lt;br /&gt;
&lt;br /&gt;
# 3 Pins notwendig.&lt;br /&gt;
# Genauigkeit vergleichbar mit einfacherer Lösung.&lt;br /&gt;
# War einfach zu faul.&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung liegt allerdings darin, dass damit&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines Digital-Analog-Konverters (&#039;&#039;&#039;DAC&#039;&#039;&#039;) können wir nun auch Analogsignale ausgeben. Es gibt hier mehrere Verfahren. &amp;lt;!-- Wenn wir beim ADC die Möglichkeit haben, mit externen Komponenten zu operieren, müssen wir bei der DAC-Wandlung mit dem auskommen, was der Controller selber zu bieten hat. --mt: hmm, richtig? verstaendlich? redundant? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DAC über mehrere digitale Ausgänge ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir an den Ausgängen des Controllers ein entsprechendes&lt;br /&gt;
Widerstandsnetzwerk aufbauen haben wir die Möglichkeit, durch die Ansteuerung&lt;br /&gt;
der Ausgänge über den Widerständen einen Addierer aufzubauen, mit dessen&lt;br /&gt;
Hilfe wir eine dem Zahlenwert proportionale Spannung erzeugen können. Das&lt;br /&gt;
Schaltbild dazu kann etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
[[Image:DAC R2R.gif]]&lt;br /&gt;
&lt;br /&gt;
Es sollten selbstverständlich möglichst genaue Widerstände verwendet&lt;br /&gt;
werden, also nicht unbedingt solche mit einer Toleranz von 10% oder mehr.&lt;br /&gt;
Weiterhin empfiehlt es sich, je nach Anwendung den Ausgangsstrom über einen&lt;br /&gt;
Operationsverstärker zu verstärken.&lt;br /&gt;
&lt;br /&gt;
=== PWM (Pulsweitenmodulation) ===&lt;br /&gt;
&lt;br /&gt;
Wir kommen nun zu einem Thema, welches in aller Munde ist, aber viele&lt;br /&gt;
Anwender verstehen nicht ganz, wie &#039;&#039;&#039; PWM&#039;&#039;&#039; eigentlich funktioniert.&lt;br /&gt;
&lt;br /&gt;
Wie wir alle wissen, ist ein Mikrocontroller ein rein digitales Bauteil.&lt;br /&gt;
Definieren wir einen Pin als Ausgang, dann können wir diesen Ausgang entweder&lt;br /&gt;
auf HIGH setzen, worauf am Ausgang die Versorgungsspannung &#039;&#039;&#039; Vcc&#039;&#039;&#039; anliegt, oder aber wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt. Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen HIGH und LOW umschalten? - Richtig, wir erhalten eine Rechteckspannung, wie die folgende Abbildung zeigt:&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 1.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Rechteckspannung hat nun einen arithmetischen Mittelwert, &lt;br /&gt;
der je nach Pulsbreite kleiner oder größer ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 2.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun diese pulsierende Ausgangsspannung noch über ein RC-Glied filtern/&amp;quot;glätten&amp;quot;, dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVRs können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. &lt;br /&gt;
Dazu dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt, weshalb ein Blick ins entsprechende Datenblatt dringend angeraten wird.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren, müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| PWM-Modus des Timers ist nicht aktiv.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Timer/Counter zählt nun permanent von 0 bis zur Obergrenze&lt;br /&gt;
und wieder zurück, er wird also als sogenannter Auf-/Ab Zähler betrieben. &lt;br /&gt;
Die Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss mit den Bits &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; desselben&lt;br /&gt;
Registers die gewünschte Ausgabeart des Signals definiert werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Nicht invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Hochzählen und gesetzt beim&lt;br /&gt;
Herunterzählen.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Herunterzählen und gesetzt beim&lt;br /&gt;
Hochzählen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl, um beispielsweise den Timer/Counter als&lt;br /&gt;
nicht invertierenden 10-Bit PWM zu verwenden, heißt dann:&lt;br /&gt;
&lt;br /&gt;
alte Schreibweise (PWMxx wird nicht mehr akzeptiert)&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;PWM11)|(1&amp;lt;&amp;lt;PWM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
neue Schreibweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;WGM11)|(1&amp;lt;&amp;lt;WGM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Timer/Counter überhaupt läuft, müssen wir im Control&lt;br /&gt;
Register B &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; noch den gewünschten Takt (Vorteiler) einstellen und&lt;br /&gt;
somit auch die Frequenz des &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signals bestimmen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stop. Der Timer/Counter wird gestoppt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin 1, negative Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin 1, positive Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Also um einen Takt von CK / 1024 zu generieren, verwenden wir&lt;br /&gt;
folgenden Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1B = (1&amp;lt;&amp;lt;CS12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt muss nur noch der Vergleichswert festgelegt werden. Diesen&lt;br /&gt;
schreiben wir in das 16-Bit Timer/Counter Output Compare Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem Vergleichswert und dem generierten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal aufzeigen.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
Ach ja, fast hätte ich&#039;s vergessen. Das generierte &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal&lt;br /&gt;
wird am Output Compare Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; des Timers ausgegeben und leider können wir&lt;br /&gt;
deshalb auch beim AT90S2313 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren. Andere AVR-Typen verfügen über bis zu vier PWM-Ausgänge. Zu beachten ist außerdem, das wenn der OC Pin aktiviert ist, er nichtmehr wie üblich funktioniert und z.B. nicht einfach über PINx ausgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Ein Programm, dass an einem ATMega8 den Fast-PWM Modus verwendet, den Modus 14, könnte so aussehen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // OC1A auf Ausgang&lt;br /&gt;
  DDRB = (1 &amp;lt;&amp;lt; PB1 );&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // Timer 1 einstellen&lt;br /&gt;
  //  &lt;br /&gt;
  // Modus 14:&lt;br /&gt;
  //    Fast PWM, Top von ICR1&lt;br /&gt;
  //&lt;br /&gt;
  //     WGM13    WGM12   WGM11    WGM10&lt;br /&gt;
  //      1        1       1        0&lt;br /&gt;
  //&lt;br /&gt;
  //    Timer Vorteiler: 1&lt;br /&gt;
  //     CS12     CS11    CS10&lt;br /&gt;
  //       0        0       1&lt;br /&gt;
  //&lt;br /&gt;
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match&lt;br /&gt;
  //     COM1A1   COM1A0&lt;br /&gt;
  //       1        0&lt;br /&gt;
 &lt;br /&gt;
  TCCR1A = (1&amp;lt;&amp;lt;COM1A1) | (1&amp;lt;&amp;lt;WGM11);&lt;br /&gt;
  TCCR1B = (1&amp;lt;&amp;lt;WGM13) | (1&amp;lt;&amp;lt;WGM12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  //  den Endwert (TOP) für den Zähler setzen&lt;br /&gt;
  //  der Zähler zählt bis zu diesem Wert&lt;br /&gt;
&lt;br /&gt;
  ICR1 = 0x6FFF;&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  // der Compare Wert&lt;br /&gt;
  // Wenn der Zähler diesen Wert erreicht, wird mit&lt;br /&gt;
  // obiger Konfiguration der OC1A Ausgang abgeschaltet&lt;br /&gt;
  // Sobald der Zähler wieder bei 0 startet, wird der&lt;br /&gt;
  // Ausgang wieder auf 1 gesetzt&lt;br /&gt;
  //&lt;br /&gt;
  // Durch Verändern dieses Wertes, werden die unterschiedlichen&lt;br /&gt;
  // PWM Werte eingestellt.&lt;br /&gt;
&lt;br /&gt;
  OCR1A = 0x3FFF;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  while( 1 )&lt;br /&gt;
    ;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für Details der PWM Möglichkeiten, muß immer das jeweilge Datenblatt des Prozessors konsultiert werden, da sich die unterschiedlichen Prozessoren in ihren Möglichkeiten doch stark unterscheiden. Auch muß man aufpassen, welches zu setzende Bit in welchem Register sind. Auch hier kann es sein, dass gleichnamige Konfigurationsbits in unterschiedlichen Konfigurationsregistern (je nach konkretem Prozessortyp) sitzen.&lt;br /&gt;
&lt;br /&gt;
= LCD-Ansteuerung =&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung an der LCD-Controller-Platine ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; (ist in manchen Unterlagen auch &#039;&#039;&#039;EN&#039;&#039;&#039;  für &#039;&#039;Enable&#039;&#039; abgekürzt) benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert, ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitetet hat (diese Methode u.a. in der LCD-Library von Peter Fleury verwendet). Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt da wir wissen, welche Anschlüsse das LCDs benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin #-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin-µC&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND oder Poti (siehe oben)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD4 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD5 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD0 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD1 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD2 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD3 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man die Steuerleitungen EN und RS auf Pins an einem anderen Port legen möchte, kann man so wie in diesem [http://www.mikrocontroller.net/topic/88543#751982 Forumsbeitrag] vorgehen.&lt;br /&gt;
&lt;br /&gt;
Ok, alles ist verbunden, wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display? &lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
void lcd_data(unsigned char temp1);&lt;br /&gt;
void lcd_string(char *data);&lt;br /&gt;
void lcd_command(unsigned char temp1);&lt;br /&gt;
void lcd_enable(void);&lt;br /&gt;
void lcd_init(void);&lt;br /&gt;
void lcd_home(void);&lt;br /&gt;
void lcd_clear(void);&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y);&lt;br /&gt;
&lt;br /&gt;
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
&lt;br /&gt;
// LCD Befehle&lt;br /&gt;
&lt;br /&gt;
#define CLEAR_DISPLAY 0x01&lt;br /&gt;
#define CURSOR_HOME   0x02&lt;br /&gt;
&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
&lt;br /&gt;
#define LCD_PORT      PORTD&lt;br /&gt;
#define LCD_DDR       DDRD&lt;br /&gt;
#define LCD_RS        PD4&lt;br /&gt;
#define LCD_EN        PD5&lt;br /&gt;
// DB4 bis DB7 des LCD sind mit PD0 bis PD3 des AVR verbunden&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// sendet ein Datenbyte an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_data(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_RS);        // RS auf 1 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_command(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);        // RS auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;              // oberes Nibble holen&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;            // maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;            // unteres Nibble holen und maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// erzeugt den Enable-Puls&lt;br /&gt;
void lcd_enable(void)&lt;br /&gt;
{&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
    _delay_us(1);                   // kurze Pause&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/80900&lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Initialisierung: &lt;br /&gt;
// Muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
void lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
   LCD_DDR = LCD_DDR | 0x0F | (1&amp;lt;&amp;lt;LCD_RS) | (1&amp;lt;&amp;lt;LCD_EN);   // Port auf Ausgang schalten&lt;br /&gt;
&lt;br /&gt;
   // muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(15);&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x03;            &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);      // RS auf 0&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4 Bit Modus aktivieren &lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x02;&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4Bit / 2 Zeilen / 5x7&lt;br /&gt;
   lcd_command(0x28);&lt;br /&gt;
    &lt;br /&gt;
   // Display ein / Cursor aus / kein Blinken&lt;br /&gt;
   lcd_command(0x0C); &lt;br /&gt;
 &lt;br /&gt;
   // inkrement / kein Scrollen&lt;br /&gt;
   lcd_command(0x06);&lt;br /&gt;
&lt;br /&gt;
   lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
&lt;br /&gt;
void lcd_clear(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CLEAR_DISPLAY);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
&lt;br /&gt;
void lcd_home(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CURSOR_HOME);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)&lt;br /&gt;
&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t tmp;&lt;br /&gt;
&lt;br /&gt;
  switch (y) {&lt;br /&gt;
    case 1: tmp=0x80+0x00+x; break;    // 1. Zeile&lt;br /&gt;
    case 2: tmp=0x80+0x40+x; break;    // 2. Zeile&lt;br /&gt;
    case 3: tmp=0x80+0x10+x; break;    // 3. Zeile&lt;br /&gt;
    case 4: tmp=0x80+0x50+x; break;    // 4. Zeile&lt;br /&gt;
  }&lt;br /&gt;
  lcd_command(tmp);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_string(char *data)&lt;br /&gt;
{&lt;br /&gt;
    while(*data) {&lt;br /&gt;
        lcd_data(*data);&lt;br /&gt;
        data++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches die Funktionen benutzt, sieht zb. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen&lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    lcd_data(&#039;T&#039;);&lt;br /&gt;
    lcd_data(&#039;e&#039;);&lt;br /&gt;
    lcd_data(&#039;s&#039;);&lt;br /&gt;
    lcd_data(&#039;t&#039;);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
&lt;br /&gt;
    lcd_string(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist dabei, dass die Optimierung bei der Compilierung eingeschaltet ist, sonst stimmen die Zeiten der Funktionen _delay_us() und _delay_ms() nicht und der Code wird wesentlich länger (Siehe Dokumentation der libc im WinAVR).&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches eine Variable ausgibt, sieht zb. so aus.&lt;br /&gt;
Mittels der itoa() Funktion (itoa = &amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;nteger &amp;lt;b&amp;gt;To&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;A&amp;lt;/b&amp;gt;scii ) wird von einem Zahlenwert eine textuelle Repräsentierung ermittelt (sprich: ein String erzeugt) und dieser String mit der bereits vorhandenen Funktion lcd_string ausgegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen &lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Beispiel&lt;br /&gt;
int variable = 42;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe des Zeichens dessen ASCII-Code gleich dem Variablenwert ist&lt;br /&gt;
    // (Im Beispiel entspricht der ASCII-Code 42 dem Zeichen *)&lt;br /&gt;
    // http://www.code-knacker.de/ascii.htm&lt;br /&gt;
    lcd_data(variable);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
 &lt;br /&gt;
    // Ausgabe der Variable als Text in dezimaler Schreibweise&lt;br /&gt;
    {&lt;br /&gt;
       // ... umwandeln siehe FAQ Artikel bei www.mikrocontroller.net&lt;br /&gt;
       // WinAVR hat eine itoa()-Funktion, das erfordert obiges #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
       char Buffer[20]; // in diesem {} lokal&lt;br /&gt;
       itoa( variable, Buffer, 10 ); &lt;br /&gt;
&lt;br /&gt;
       // ... ausgeben  &lt;br /&gt;
       lcd_string( Buffer );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einrichten eines Projekts muss man zu der Datei mit dem Hauptprogramm auch die Datei lcd-routines.c in das Projekt aufnehmen. Dies geschieht beim AVR Studio unter Source Files im Fenster AVR GCC oder bei WinAVR im Makefile (z.B. durch SRC += lcd-routines.c).&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVRs sind für viele Steuerungsaufgaben zu schnell. Wenn wir beispielsweise eine LED oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die CPU-Frequenz verwenden, da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, Vorgänge in Zeitabständen durchzuführen, die geringer als die Taktfrequenz des Controllers sind. Selbstverständlich sollte die resultierende Frequenz auch noch möglichst genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist ganz einfach ein bestimmtes Register im µC, das völlig ohne Zutun des Programms, also per Hardware, hochgezählt wird. Das alleine wäre noch nicht allzu nützlich, wenn nicht dieses Hardwareregister bei bestimmten Zählerständen einen Interrupt auslösen könnte. Ein solches Ereignis ist der Overflow: Da die Bitbreite des Registers beschränkt ist, kommt es natürlich auch vor, dass der Zähler so hoch zählt, dass der nächste Zählerstand mit dieser Bitbreite nicht mehr darstellbar ist und der Zähler wieder auf 0 zurückgesetzt wird. Dieses Ereignis nennt man den Overflow und es ist möglich an dieses Ereignis einen Interrupt zu koppeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Alternativvorschlag mthomas &lt;br /&gt;
Jeder Timer verfügt über ein Zählerregister im Mikrocontroller, das automatisch und ohne Zutun des Programms von der Hardware weitergezählt wird. In einem einfachen Anwendungsfall stellt man den Timer auf eine Zählgeschwindigkeit (Frequenz) und kann dann anhand des Zählerstands ermitteln, wie viel Zeit vergangen ist. Das eigentlich Nützliche an Timern ist jedoch, dass man  bestimmte Zählerstände mit Interrupts verknüpfen kann, so dass der Controller beim Auftreten automatisch eine vom Anwender geschriebene Routine aufruft. Eines dieser möglichen Ereignis ist der Overflow ((Zähler-)Überlauf), der dann auftritt, wenn der Wert des Zählerregisters (Timer/Counter-Register) den maximal möglichen Wert überschreitet. Der Maximalwert wird durch die Bitbreite des Zählerregisters bestimmt (z.B. 255 bei 8-Bit Timern). Beim Überlauf/Overflow wird der Zähler durch die Hardware auf 0 zurückgesetzt und die Zählung beginnt von neuem. Wurde vorher der Overflow-Interrupt für den Timer aktiviert (im Timer Control Register) unterbricht der Controller automatisch die Ausführung des Hauptprogramms und verzweigt in die Interrupt-Routine des Anwenders.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auf den AT90S2313. Für andere Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer Auflösung von 65536. Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz, der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
== Der Vorteiler (Prescaler) ==&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler dient dazu, den CPU-Takt vorerst um einen einstellbaren Faktor zu reduzieren. Die so geteilte Frequenz wird den Eingängen der Timer zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024 einstellen, wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca. 4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw. Zählregister (TCNTx) mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle verfügen über mindestens einen, teilweise sogar zwei, 8-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird z.B bei AT90S2313 über folgende Register angesprochen (bei anderen Typen weitestgehend analog):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    TCCR0 |= (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst und die daran gebundene Interrupt-Routine abgearbeitet. Das TOV Flag &lt;br /&gt;
lässt sich durch das Hineinschreiben einer 1 und nicht wie erwartet einer 0 wieder zurücksetzen.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen außer den 8-Bit Timern auch 16-Bit Timer. Die 16-Bit Timer/Counter sind etwas komplexer aufgebaut als die 8-Bit Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM13&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM12 (CTC1)&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorteiler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt, und C der voreingestellte Vergleichswert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin T1, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin T1, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T1 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T1 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0, bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;TCNT1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;OCR1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;OCR1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;OCR1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;OCR1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäß folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]] [[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Ausserdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr. Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen kleiner 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4us warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.6 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t x;&lt;br /&gt;
&lt;br /&gt;
    x = 10;&lt;br /&gt;
&lt;br /&gt;
    while (x &amp;gt;= 0)&lt;br /&gt;
    {&lt;br /&gt;
      // tu was&lt;br /&gt;
&lt;br /&gt;
      x--;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog? ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Einige Controller haben einen eigenen Watchdog Oszillator, z.B. der Tiny2313 mit 128kHz. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde, beginnt der Counter von 0 an hochzuzählen. &lt;br /&gt;
Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern, müssen wir den Watchdog regelmäßig wieder neu starten bzw. rücksetzen (Watchdog Reset). &lt;br /&gt;
Dies sollte innerhalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern, muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten. Es müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. &lt;br /&gt;
Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist, wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei &#039;&#039;wdt.h&#039;&#039; (&#039;&#039;#include &amp;lt;avr/wdt.h&amp;gt;&#039;&#039;) in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch gestartet wird. &lt;br /&gt;
Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. &lt;br /&gt;
Falls ein anderer Wert gewünscht ist, so kann dies im Makfile in den Linker-Optionen eingetragen werden. &lt;br /&gt;
Dazu muss in der Zeile LDFLAGS folgende Option angefügt werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert&lt;br /&gt;
:Mögliche Timeoutwerte:&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Konstante&lt;br /&gt;
! Wert&lt;br /&gt;
! TimeOut&lt;br /&gt;
|- &lt;br /&gt;
| WDTO_15MS   &lt;br /&gt;
| 0&lt;br /&gt;
| 15 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_30MS   &lt;br /&gt;
| 1&lt;br /&gt;
| 30 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_60MS   &lt;br /&gt;
| 2&lt;br /&gt;
| 60 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_120MS   &lt;br /&gt;
| 3&lt;br /&gt;
| 120 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_250MS   &lt;br /&gt;
| 4&lt;br /&gt;
| 250 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_500MS   &lt;br /&gt;
| 5&lt;br /&gt;
| 500 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_1S   &lt;br /&gt;
| 6&lt;br /&gt;
| 1 S&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_2S   &lt;br /&gt;
| 7&lt;br /&gt;
| 2s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
== Watchdog-Anwendungshinweise ==&lt;br /&gt;
&lt;br /&gt;
Ob nun der Watchdog als Schutzfunktion überhaupt verwendet werden soll, hängt stark von der Anwendung, der genutzten Peripherie und dem Umfang und der Qualitätssicherung des Codes ab. Will man sicher gehen, dass ein Programm sich nicht in einer Endlosschleife verfängt, ist der Wachdog das geeignete Mittel dies zu verhindern. Weiterhin kann bei geschickter Programmierung der Watchdog dazu genutzt werden, bestimmte Stromsparfunktionen zu implementieren. Bei einigen neueren AVRs (z.B. dem ATTiny13) kann der Watchdog auch direkt als Timer genutzt werden, der den Controller aus einem Schlafmodus aufweckt. Auch dies kann im &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register eingestellt werden. Außerdem bietet der WD die einzige Möglichkeit einen beabsichtigten System-Reset (ein &amp;quot;richtiger Reset&amp;quot;, kein &amp;quot;jmp 0x0000&amp;quot;) ohne externe Beschaltung auszulösen, was z.B. bei der Implementierung eines Bootloaders nützlich ist. Bei bestimmten Anwendungen kann die Nutzung des WD als &amp;quot;ultimative Deadlock-Sicherung für nicht bedachte Zustände&amp;quot; natürlich immer als zusätzliche Sicherung dienen. &lt;br /&gt;
&lt;br /&gt;
Es besteht die Möglichkeit herauszufinden, ob ein Reset durch den Watchdog ausgelöst wurde (beim ATmega16 z.B. Bit WDRF in MCUCSR). Diese Information sollte auch genutzt werden, falls ein WD-Reset in der Anwendung nicht planmäßig implementiert wurde. Zum Beispiel kann man eine LED an einen freien Pin hängen, die nur bei einem Reset durch den WD aufleuchtet oder aber das &amp;quot;Ereignis WD-Reset&amp;quot; im internen EEPROM des AVR absichern, um die Information später z.B. über UART oder ein Display auszugeben (oder einfach den EEPROM-Inhalt über die ISP/JTAG-Schnittstelle auslesen).&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/77273#642501 Bug in ATtiny2313?]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt der Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft. Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe Abschnitt (TODO: verlinken) im Anhang.&lt;br /&gt;
&amp;lt;!--Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. vgl. [[AVR-GCC-Tutorial#Anhang|Anhang]])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.B. &#039;&#039;void TIMER0_OVF_vect(void)...&#039;&#039;). Der Unterschied im Vergleich zu ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben.  &amp;lt;!--Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)), als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur &lt;br /&gt;
in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechnungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechnungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0, PORTA ist danach 0b00000001. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-oder-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8 (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten &#039;&#039;Sleep-Modes&#039;&#039; (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_enable()&#039;&#039;&#039;&lt;br /&gt;
:aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_cpu()&#039;&#039;&#039;&lt;br /&gt;
: Versetzt den Controller in den Schlafmodus (sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt)&lt;br /&gt;
* &#039;&#039;&#039;sleep_disable()&#039;&#039;&#039;&lt;br /&gt;
:deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts.&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;allozieren&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich, um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory, bei AVR Controllern mit mehr als 64kB Flash auch elpm). Die Standard-Laufzeitbibliothek des avr-gcc (die avr-libc) stellt diese Funktionen nach Einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Lokale Variablen (eigentlich Konstanten) innerhalb von Funktionen können ebenfalls im Programmspeicher abgelegt werden. Dazu ist bei der Definition jedoch ein &#039;&#039;static&#039;&#039; voranzustellen, da solche &amp;quot;Variablen&amp;quot; nicht auf dem Stack bzw. (bei Optimierung) in Registern verwaltet werden können. Der Compiler &amp;quot;wirft&amp;quot; eine Warnung falls static fehlt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray2 };&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  static /*const*/ uint8_t pgmTestByteLocal PROGMEM = 0x55;&lt;br /&gt;
  static /*const*/ char pgmTestStringLocal[] PROGMEM = &amp;quot;im Flash&amp;quot;;&lt;br /&gt;
  // so nicht (static fehlt): char pgmTestStringLocalFalsch [] PROGMEM = &amp;quot;so nicht&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 // ...&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
    //...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte....&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWort);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;groß&amp;quot; (Stand avr-gcc 3.4.x). Damit ist der mögliche Speicherbereich für &amp;quot;Flash-Konstanten&amp;quot; auf 64kB begrenzt.  &amp;lt;!-- Einige avr-libc/pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher) (intern via Assembler Anweisung ELPM). Die Initialisierungswerde des Speicherinhalts jenseits der 64kB-Marke müssen dann jedoch auf anderem Weg angelegt werden (nicht PROGMEM, evtl. eigene Section und Linker-Optionen - TODO) /// alt - und nicht ganz korrekt: (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.)--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray enthält nun die Startadresse des Byte-Arrays pgmFooByteArray1&lt;br /&gt;
    // Allerdings würde ein direkter Zugriff mit diesem Pointer (z.B. temp=*ptrToArray)&lt;br /&gt;
    // &#039;&#039;&#039;nicht&#039;&#039;&#039; den Inhalt von pgmFooByteArray1[0] liefern, sondern von einer Speicherstelle&lt;br /&gt;
    // im &#039;&#039;&#039;RAM&#039;&#039;&#039;, die die gleiche Adresse hat wie pgmFooByteArray1[0]&lt;br /&gt;
    // Daher muss nun die Funktion pgm_read_byte() benutzt werden, die die in ptrToArray&lt;br /&gt;
    // enthaltene Adresse benutzt und auf das Flash zugreift.&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray enthält nun die Adresse des ersten Elements des Byte-Arrays pgmFooByteArray2&lt;br /&gt;
    // da im zweiten Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmFooByteArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Array aus Zeichenketten im Flash-Speicher ===&lt;br /&gt;
&lt;br /&gt;
Felder aus Zeichenketten im Flash-Speicher werden in zwei Schritten angelegt: Zuerst die einzelnen Elemente des Arrays und im Anschluss ein Array, in dem die Addressen der Zeichenketten abgelegt werden. Zum Auslesen wird zuerst die Adresse des i-ten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, auf das Element (die Zeichenkette) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const char str1[] PROGMEM = &amp;quot;first_A&amp;quot;;&lt;br /&gt;
const char str2[] PROGMEM = &amp;quot;second_A&amp;quot;;&lt;br /&gt;
const char str3[] PROGMEM = &amp;quot;third_A&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char *strarray1[] PROGMEM = {&lt;br /&gt;
	str1,&lt;br /&gt;
	str2,&lt;br /&gt;
	str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	int i, j, l;&lt;br /&gt;
	const char *pstrflash;&lt;br /&gt;
	char work[20], work2[20];&lt;br /&gt;
	// fuer Simulation: per volatile Optimierung verhindern, &lt;br /&gt;
	//                  da c nicht genutzt&lt;br /&gt;
	volatile char c;&lt;br /&gt;
	&lt;br /&gt;
	for ( i = 0; i &amp;lt; (sizeof(strarray1)/sizeof(strarray1[0]) ); i++ ) {&lt;br /&gt;
&lt;br /&gt;
		// setze Pointer auf die Addresse des i-ten Elements des&lt;br /&gt;
		// &amp;quot;Flash-Arrays&amp;quot; (str1, str2, ...)&lt;br /&gt;
		pstrflash = (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) );&lt;br /&gt;
		&lt;br /&gt;
		// kopiere den Inhalt der Zeichenkette von der&lt;br /&gt;
		// in pstrflash abgelegten Adresse in das work-Array&lt;br /&gt;
		// analog zu strcpy( work, strarray1[i]) wenn alles im RAM&lt;br /&gt;
		strcpy_P( work, pstrflash );&lt;br /&gt;
		// verkuerzt:&lt;br /&gt;
		strcpy_P( work2, (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) ) );&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
		// Zeichen-fuer-Zeichen&lt;br /&gt;
		l = strlen_P( pstrflash );&lt;br /&gt;
		for ( j=0; j &amp;lt; l; j++ ) {&lt;br /&gt;
			// analog zu c=strarray[i][j] wenn alles im RAM&lt;br /&gt;
			c = (char)( pgm_read_byte( pstrflash++ ) );&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	while (1) { ; }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: &amp;quot;How do I put an array of strings completely in ROM?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5)) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Vorsicht: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char textImFlashOK[] PROGMEM = &amp;quot;mit[]&amp;quot;; &lt;br /&gt;
// = Daten im &amp;quot;Flash&amp;quot;, textImFlashOK* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char* textImFlashProblem PROGMEM = &amp;quot;mit*&amp;quot;;&lt;br /&gt;
// Konflikt: Daten im BSS (lies: RAM), textImFlashFAIL* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der Initialisierungsstring von &amp;quot;textImFlashProblem&amp;quot; zu den Konstanten ans Ende des Programmcodes gelegt wird (BSS), von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte (und wird). Da der lesende Code (mittels pgm_read*) trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein weiters Problem des AVR-GCC (gesehen bei avr-gcc 3.4.1 und 3.4.2) bei der Anpassung an die Harvard-Architektur zu sein (konstanter Pointer auf variable Daten?!). Abhilfe (&amp;quot;Workaround&amp;quot;): Initialisierung bei Zeichenketten mit [] oder gleich im Code PSTR(&amp;quot;...&amp;quot;) nutzen.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden, ob es sich um eine Adresse im Flash oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind.&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die einen im Flash abgelegten String z.B. an eine UART ausgibt, würde dann so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void uart_puts_p(const char *text)&lt;br /&gt;
{&lt;br /&gt;
    char Zeichen;&lt;br /&gt;
&lt;br /&gt;
    while (Zeichen = pgm_read_byte(text))&lt;br /&gt;
    {   /* so lange, wie mittels pgm_read_byte ein Zeichen vom Flash gelesen&lt;br /&gt;
           werden konnte, welches nicht das &amp;quot;String-Endezeichen&amp;quot; darstellt */&lt;br /&gt;
&lt;br /&gt;
        /* Das gelesene Zeichen über die normalen Kanäle verschicken */&lt;br /&gt;
        uart_putc(Zeichen);&lt;br /&gt;
        text++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Flash in der Anwendung schreiben ===&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option (auch bekannt als Bootloader-Support) können Teile des Flash-Speichers auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. Bei wenigen &amp;quot;kleinen&amp;quot; AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm(Flash)- und Datenspeicher(RAM) auf. Der C-Standard und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiß&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, dass vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgegeben sind, die innerhalb weniger Taktzyklen aufeinanderfolgen müssen (&amp;quot;timed sequence&amp;quot;). Auch dies muss bei Nutzung der Funktionen aus der avr-libc/eeprom.h-Datei nicht selbst implementiert werden. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Siehe dazu folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt; // wird in aktuellen Versionen der avr-lib mit xx.h eingebunden&lt;br /&gt;
&lt;br /&gt;
// EEMEM wird bei aktuellen Versionen der avr-lib in eeprom.h definiert&lt;br /&gt;
// hier: definiere falls noch nicht bekannt (&amp;quot;alte&amp;quot; avr-libc)&lt;br /&gt;
#ifndef EEMEM&lt;br /&gt;
// alle Textstellen EEMEM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEMEM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
//...&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // &#039;Variablen&#039; eeFooByte geschrieben&lt;br /&gt;
//...&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z.B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    #define EEPROM_DEF 0xFF&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ( ( myByte = eeprom_read_byte(&amp;amp;eeFooByte) ) == EEPROM_DEF ) {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
//...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprüft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und möglicherweise noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,3);&lt;br /&gt;
&lt;br /&gt;
    /* dito etwas anschaulicher aber &amp;quot;unnütze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fließkommazahlen lassen sich recht praktisch über eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t i[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    uint8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEMEM;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
//...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklaration abgeleiteten EEPROM-Inhalt in eine Datei geschrieben werden (übliche Dateiendung: .eep, Daten im Intel Hex-Format). Damit können recht elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen (siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles). Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden (Write EEPROM), wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse (vgl. Datenblatt Abschnitt Fuse Bits) die vorherigen Daten (EESAVE programmed = 0), deren Position möglicherweise nicht mehr mit der Belegung im aktuellen Programm übereinstimmt oder den Standardwert nach &amp;quot;Chip Erase&amp;quot;: 0xFF (EESAVE unprogrammed = 1). Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Eine besondere Funktion des avr-gcc ist, dass mit entsprechenden Optionen im Makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Variable auf feste Adressen legen ===&lt;br /&gt;
&lt;br /&gt;
Gleich zu Beginn möchte ich darauf hinweisen, dass dieses Verfahren nur ein Workaround ist, mit dem man das Problem der anscheinend &amp;quot;zufälligen&amp;quot; Verteilung&lt;br /&gt;
der EEPROM-Variablen durch den Compiler etwas in den Griff bekommen kann.&lt;br /&gt;
&lt;br /&gt;
Hilfreich kann dies vor allem dann sein, wenn man z.B. über einen Kommandointerpreter (o.ä. Funktionen) direkt bestimmte EEPROM-Adressen manipulieren möchte. Auch wenn man über einen JTAG-Adapter (mk I oder mkII) den Programmablauf manipulieren möchte, indem man die EEPROM-Werte direkt ändert, kann diese Technik hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Im folgenden nun zwei Sourcelistings mit einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.h&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#inlcude &amp;lt;avr/eeprom.h&amp;gt;     // Die EEPROM-Definitionen/Macros der avr-libc einbinden&lt;br /&gt;
&lt;br /&gt;
#define   EESIZE   512      // Maximale Größe des EEPROMS&lt;br /&gt;
&lt;br /&gt;
#define   EE_DUMMY   0x000  // Dummyelement (Adresse 0 sollte nicht genutzt werden)&lt;br /&gt;
#define   EE_VALUE1  0x001  // Eine Bytevariable  &lt;br /&gt;
#define   EE_WORD1L  0x002  // Eine Wordvariable (Lowbyte)&lt;br /&gt;
#define   EE_WORD1H  0x003  // Eine Wordvariable (Highbyte)&lt;br /&gt;
#define   EE_VALUE2  0x004  // Eine weitere Bytevariable&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den Macros &#039;&#039;&#039;#define EE_VALUE1&#039;&#039;&#039; legt man den Namen und die Adresse der&lt;br /&gt;
&#039;Variablen&#039; fest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Die Adressen sollten fortlaufend, zumindest aber aufsteigend sortiert sein! Ansonsten besteht die Gefahr, daß man sehr schnell ein Durcheinander im EEPROM Speicher veranstaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Für den Compiler sind das lediglich Speicher-Adressen, über die auf das EEPROM zugegriffen wird. Der Compiler sieht nichts davon als eine echte Variable an und stößt sich daher auch nicht daran, wenn 2 Makros mit der gleichen Speicheradresse, bzw. überlappenden Speicherbereichen definiert werden. Es liegt einzig und alleine in der Hand des Programmierers, hier keinen Fehler zu machen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
   [EE_DUMMY]   = 0x00,&lt;br /&gt;
   [EE_VALUE1]  = 0x05,&lt;br /&gt;
   [EE_WORD1L]  = 0x01,   &lt;br /&gt;
   [EE_WORD1H]  = 0x00,&lt;br /&gt;
   [EE_VALUE2]  = 0xFF&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch die Verwendung eines Array, welches das gesammte EEPROM umfasst, bleibt&lt;br /&gt;
dem Compiler nicht anderes übrig, als das Array so zu platzieren, dass Element 0&lt;br /&gt;
des Arrays der Adresse 0 des EEPROMs entspricht. (&#039;&#039;Ich hoffe nur, dass die Compilerbauer daran nichts ändern!&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Wie man in dem obigen Codelisting auch sehen kann, hat das Verfahren einen kleinen Haken. Variablen die größer sind als 1 Byte, müssen etwas umständlicher&lt;br /&gt;
definiert werden. Benötigt man keine Initialisierung durch das Programm (was der Normalfall sein dürfte), dann kann man das auch so machen:&lt;br /&gt;
&lt;br /&gt;
Möchte man im EEPROM hintereinander beispielsweise Variablen, mit den Namen &#039;&#039;&#039;Wert&#039;&#039;&#039;, &#039;&#039;&#039;Anzahl&#039;&#039;&#039;, &#039;&#039;&#039;Name&#039;&#039;&#039; und &#039;&#039;&#039;Wertigkeit&#039;&#039;&#039; definieren, wobei Wert und Wertigkeit 1 Byte belegen sollen, Anzahl als 1 Wort (also 2 Bytes) und Name mit 10 Bytes reserviert werden soll, so geht auch folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes Makro definiert also seine Startadresse durch die Startadresse der unmittelbar vorhergehende &#039;Variablen&#039; plus der Anzahl der Bytes die von der vorhergehenden &#039;Variablen&#039; verbraucht werden. Dadurch ist man zumindest etwas auf der sicheren Seite, dass keine 2 &#039;Variablen&#039; im EEPROM überlappend definiert werden. Möchte man eine weitere &#039;Variable&#039; hinzufügen, so wird deren&lt;br /&gt;
Name, einfach anstelle der EE_LAST eingesetzt und eine neue Zeile für EE_LAST eingefügt, in der dann die Größe der &#039;Variablen&#039; festgelegt wird. Zb.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_PROZENT    ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( double ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EE_PROZENT legt die Startadresse für eine neue &#039;Variable&#039; des Datentyps double fest.&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf die EEPROM Werte kann dann z.B.so erfolgen:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t   temp1;&lt;br /&gt;
uint16_t  temp2;&lt;br /&gt;
&lt;br /&gt;
temp1 = eeprom_read_byte(EE_VALUE1);&lt;br /&gt;
temp2 = eeprom_read_word(EE_WORD1L);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ob die in der avr-libc vorhandenen Funktionen dafür verwendet werden können, weiß ich nicht. Aber in einigen Fällen muss man sich sowieso eigene Funktionen&lt;br /&gt;
bauen, welche die spezifischen Anforderungen (Interrupt - Atom Problem, etc.)&lt;br /&gt;
erfüllen.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist nur eine Möglichkeit, wie man dies realisieren kann. Sie bietet einem eine relativ einfache Art die EEPROM-Werte&lt;br /&gt;
auf beliebige Adressen zu legen oder Adressen zu ändern. Die Andere Möglichkeit besteht darin, die EEPROM-Werte wie folgt zu belegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
  0x00,                     //  ee_dummy&lt;br /&gt;
  0x05,                     //  ee_value1&lt;br /&gt;
  0x01,                     //  ee_word1L&lt;br /&gt;
  0x00,                     // (ee_word1H)&lt;br /&gt;
  0xFF                      //  ee_value2&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei kann man Variablen, die größer sind als 1 Byte einfacher definieren und&lt;br /&gt;
man muss nur die Highbyte- oder Lowbyte-Adresse in der &amp;quot;eeprom.h&amp;quot; definieren.&lt;br /&gt;
Allerdings muss man hier höllisch aufpassen, dass man nicht um eine oder mehrere&lt;br /&gt;
Positionen verrutscht!&lt;br /&gt;
&lt;br /&gt;
Welche der beiden Möglichkeiten man einsetzt, hängt vor allem davon ab, wieviele&lt;br /&gt;
Byte, Word und sonstige Variablen man benutzt. Gewöhnen sollte man sich an beide Varianten können ;)&lt;br /&gt;
&lt;br /&gt;
Kleine Schlussbemerkung:&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc unterstützt die Variante 1 und die Variante 2&lt;br /&gt;
* Der icc-avr Compiler unterstützt nur die Variante 2!&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Bei alten Versionen der avr-libc wurden nicht alle AVR Controller  untersützt. Z.B. bei der avr-libc Version 1.2.3 insbesondere bei AVRs &amp;quot;der neuen Generation&amp;quot; (ATmega48/88/168/169) funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register). In neueren Versionen (z.B. avr-libc 1.4.3 aus WinAVR 20050125) wurde die Zahl der unterstüzten Controller deutlich erweitert und eine Methode zur leichten Anpassung an zukünftige Controller eingeführt.&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Will oder kann man nicht auf die neue Version aktualisieren, kann der dort gezeigte Code auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;, gegebenfalls Schutz vor Unterbrechnung/Interrupt ergänzen &#039;&#039;uint8_t sreg; sreg=SREG; cli(); [EEPROM-Code] ; SREG=sreg; return;&#039;&#039;, siehe Abschnitt Interrupts). Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== EEPROM Register ===&lt;br /&gt;
Um das EEPROM anzusteuern sind drei Register von Bedeutung.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEAR Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL da in einem 8 Bit Register keine 512 Adressen adressiert werden können&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEDR Hier werden die Daten eingetragen die geschrieben werden sollen bzw. es enthält die gelesenen Daten&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EECR Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TABLE BORDER=1&amp;gt;&lt;br /&gt;
&amp;lt;TR&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;Bit&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;1&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;0&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&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;Name&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&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;TD&amp;gt;-&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERIE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEMWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERE&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;Read/Write&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;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&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;Init Value&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;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;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;TD&amp;gt;0&amp;lt;/TD&amp;gt;&lt;br /&gt;
&amp;lt;/TR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/TABLE&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bedeutung der Bits:&amp;lt;/U&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4-7 nicht belegt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 3 (EERIE) - EEPROM Ready Interrupt Enable:&amp;lt;/U&amp;gt; Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7) wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0 wird kein Interrupt ausgelöst.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 2(EEMWE) - EEPROM Master Write Enable:&amp;lt;/U&amp;gt; Dieses Bit bestimmt, daß wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE =0 ist und EEWE = 1 gesetzt wird hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst.&lt;br /&gt;
Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 1 (EEWE) - EEPROM Write Enable:&amp;lt;/U&amp;gt; Dieses Bit löst den Schreibvorgang aus wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist wird dieses Bit automatisch wieder auf 0 gesetzt und sofern EERIE gesetzt ist ein Interrupt ausgelöst. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ein Schreibvorgang sieht typischerweise wie folgt aus:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. EEPROM Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Daten übergeben an EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1 &amp;lt;BR&amp;gt;&lt;br /&gt;
5. (Optinal) Warten bis Schreibvorgang abgeschlossen ist.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 0 EERE – EEPROM Read Enable:&amp;lt;/U&amp;gt; Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit wenn das Bit EEWE=0 ist. ISt der LEsevorgang abgeschlossen wird das Bit wieder auf 0 gesetzt und das EEPROM ist für neue Lese/Schreibbefehle wieder bereit.&amp;lt;BR&amp;gt;&lt;br /&gt;
Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. Bereitschaft zum lesen prüfen (EEWE=0)&amp;lt;BR&amp;gt;&lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Lesezyklus auslösen mit EERE = 1&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Warten bis Lesevorgang abgeschlossen EERE = 0&amp;lt;BR&amp;gt;&lt;br /&gt;
5. Daten abholen aus EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bietet sich printf/sprintf an. &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t s[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( s, &amp;quot;Zählerstand: %d&amp;quot;, value );&lt;br /&gt;
    uart_puts( s );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, die Ausgabe stdout auf eigene Ausgabefunktionen umzuleiten. Dazu wird dem Ausgabemechanismus eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben (wie auch immer die Ausgabe stattfindet). Alle anderen, höheren, Funktionen greifen letztendlich auf diese eine Ausgabefunktion zurück, die die Ausgabe eines einzelnen Zeichens vornimmt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uart_init();&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
    printf( &amp;quot;Hello, world!\n&amp;quot; );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil aller printf-Varianten: Sie sind recht speicherintensiv.&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C- und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gnu-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Inline-Assembler: Die Assembleranweisungen werden direkt in den C-Code integriert. Eine Quellcode-Datei enthält somit C- und Assembleranweisungen&lt;br /&gt;
* Assembler-Dateien: Der Assemblercode befindet sich in eigenen Quellcodedateien. Diese werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung (NOP steht für No Operation). Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=Warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
   /* Verzögern der weiteren Programmausführung um&lt;br /&gt;
      genau 3 Taktzyklen */&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeintlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
&lt;br /&gt;
    /* leere Schleife - wird bei eingeschalteter Compiler-Optimierung   wegoptimiert */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* alternative Methode (keine Optimierung): */&lt;br /&gt;
    volatile uint16_t j;&lt;br /&gt;
    for (j = 0; j &amp;lt; 1000; j++)&lt;br /&gt;
      ;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützlicher &amp;quot;Assembler-Einzeiler&amp;quot; ist der Aufruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;);&#039;&#039;), da hierzu in älteren Versionen der avr-libc keine eigene Funktion existiert (in neueren Versionen &#039;&#039;sleep_cpu()&#039;&#039; aus sleep.h).&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise Delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil, dass die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static inline void delayloop16 (uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
    asm volatile (&amp;quot;cp  %A0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;cpc %B0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;breq 2f               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;1:                    \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;sbiw %0,1             \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;brne 1b               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;2:                    &amp;quot;  &lt;br /&gt;
                  : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
	          : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
    );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Anweisung wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen. Der Zeilenumbruch teilt dem Assembler mit, dass ein neuer Befehl beginnt.&lt;br /&gt;
* Als Sprung-Marken (Labels) werden Ziffern verwendet. Diese speziellen Labels sind mehrfach im Code verwendbar. Gesprungen wird jeweils zurück (b) oder vorwärts (f) zum nächsten ausffindbaren Label.&lt;br /&gt;
&lt;br /&gt;
Das Resultat zeigt ein Blick in die Assembler-Datei, die der Compiler mit der option &amp;lt;tt&amp;gt;-save-temps&amp;lt;/tt&amp;gt; nicht löscht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	cp  r24, __zero_reg__ 	 ;  count&lt;br /&gt;
	cpc r25, __zero_reg__ 	 ;  count&lt;br /&gt;
	breq 2f               &lt;br /&gt;
	1:                    &lt;br /&gt;
	sbiw r24,1             	 ;  count&lt;br /&gt;
	brne 1b               &lt;br /&gt;
	2:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc im Abschnitt [http://www.nongnu.org/avr-libc/user-manual/inline_asm.html Related Pages/Inline Asm]. &lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc Deutsche Einführung in Inline-Assembler]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
Wenn man mit dem AVR Studio arbeitet, kann alternativ auch das standardmäßig erstellte Makefile bearbeitet und folgende Zeilen eingefügt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
## Objects that must be built in order to link&lt;br /&gt;
OBJECTS = (alte Dateien...) useful.o&lt;br /&gt;
&lt;br /&gt;
## Compile&lt;br /&gt;
## Hier folgt eine Liste der gelinkten Dateien, darunter einfügen:&lt;br /&gt;
useful.o: ../useful.S&lt;br /&gt;
	$(CC) $(INCLUDES) $(ASMFLAGS) -c  $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Das war es schon. Allerdings gilt es zu beachten, dass das makefile über &amp;quot;Project -&amp;gt; Configuration options&amp;quot; selbst einzubinden ist, sonst wird es natürlich wieder überschrieben.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel eine Funktion &#039;&#039;superFunc&#039;&#039;, die alle Pins des Ports D auf &amp;quot;Ausgang&amp;quot; schaltet, eine Funktion &#039;&#039;ultraFunc&#039;&#039;, die die Ausgänge entsprechend des übergebenen Parameters schaltet, eine Funktion &#039;&#039;gigaFunc&#039;&#039;, die den Status von Port A zurückgibt und eine Funktion &#039;&#039;addFunc&#039;&#039;, die zwei Bytes zu einem 16-bit-Wort addiert. Die Zuweisungen im C-Code (PORTx = ...) verhindern, dass der Compiler die Aufrufe wegoptimiert und dienen nur zur Veranschaulichung der Parameterübergaben.&lt;br /&gt;
&lt;br /&gt;
Zuerst der Assembler-Code. Der Dateiname sei useful.S:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//; Arbeitsregister (ohne &amp;quot;r&amp;quot;) &lt;br /&gt;
workreg  = 16&lt;br /&gt;
workreg2 = 17&lt;br /&gt;
&lt;br /&gt;
//; Konstante:&lt;br /&gt;
ALLOUT = 0xff&lt;br /&gt;
&lt;br /&gt;
//; ** Setze alle Pins von PortD auf Ausgang **&lt;br /&gt;
//; keine Parameter, keine Rückgabe&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg  // beachte: _SFR_IO_ADDR()&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Setze PORTD auf übergebenen Wert **&lt;br /&gt;
//; Parameter in r24 (LSB immer bei &amp;quot;graden&amp;quot; Nummern)&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zustand von PINA zurückgeben **&lt;br /&gt;
//; Rückgabewerte in r24:r25 (LSB:MSB), hier nur LSB genutzt&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zwei Bytes addieren und 16-bit-Wort zurückgeben **&lt;br /&gt;
//; Parameter in r24 (Summand1) und r22 (Summand2) -&lt;br /&gt;
//;  Parameter sind Word-&amp;quot;aligned&amp;quot; d.h. LSB immer auf &amp;quot;graden&amp;quot;&lt;br /&gt;
//;  Registernummern. Bei 8-Bit und 16-Bit Paramtern somit &lt;br /&gt;
//;  beginnend bei r24 dann r22 dann r20 etc.&lt;br /&gt;
//; Rückgabewert in r24:r25&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   push workreg2&lt;br /&gt;
   clr workreg2&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
   pop workreg2&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
//; oh je - sorry - Mein AVR-Assembler ist eingerostet, hoffe das stimmt so...&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Makefile ist der Name der Assembler-Quellcodedatei einzutragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
ASRC = useful.S&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Aufruf erfolgt dann im C-Code so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
extern void superFunc(void);&lt;br /&gt;
extern void ultraFunc(uint8_t setVal);&lt;br /&gt;
extern uint8_t gigaFunc(void);&lt;br /&gt;
extern uint16_t addFunc(uint8_t w1, uint8_t w2);&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
[...]&lt;br /&gt;
  superFunc();&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
&lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
[...]&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird wieder in der lss-Datei ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
   superFunc();&lt;br /&gt;
 148:	0e 94 f6 00 	call	0x1ec&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
 14c:	85 e5       	ldi	r24, 0x55	; 85&lt;br /&gt;
 14e:	0e 94 fb 00 	call	0x1f6&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
 152:	0e 94 fd 00 	call	0x1fa&lt;br /&gt;
 156:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
  &lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
 158:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 15a:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 15c:	0e 94 ff 00 	call	0x1fe&lt;br /&gt;
 160:	8b bb       	out	0x1b, r24	; 27&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
 162:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 164:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 166:	0e 94 fc 00 	call	0x1f8&lt;br /&gt;
 16a:	89 2f       	mov	r24, r25&lt;br /&gt;
 16c:	99 27       	eor	r25, r25&lt;br /&gt;
 16e:	88 bb       	out	0x18, r24	; 24&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
000001ec &amp;lt;superFunc&amp;gt;:&lt;br /&gt;
// setze alle Pins von PortD auf Ausgang&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1ec:	0f 93       	push	r16&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
 1ee:	0f ef       	ldi	r16, 0xFF	; 255&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg&lt;br /&gt;
 1f0:	01 bb       	out	0x11, r16	; 17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 1f2:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 1f4:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001f6 &amp;lt;ultraFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// setze PORTD auf übergebenen Wert&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
 1f6:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
   ret&lt;br /&gt;
 1f8:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fa &amp;lt;gigaFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Zustand von PINA zurückgeben&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
 1fa:	89 b3       	in	r24, 0x19	; 25&lt;br /&gt;
   ret&lt;br /&gt;
 1fc:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fe &amp;lt;addFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// zwei Bytes addieren und 16-bit-Wort zurückgeben&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1fe:	0f 93       	push	r16&lt;br /&gt;
   push workreg2&lt;br /&gt;
 200:	1f 93       	push	r17&lt;br /&gt;
   clr workreg2&lt;br /&gt;
 202:	11 27       	eor	r17, r17&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
 204:	06 2f       	mov	r16, r22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
 206:	08 0f       	add	r16, r24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
 208:	11 1d       	adc	r17, r1&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
 20a:	c8 01       	movw	r24, r16&lt;br /&gt;
   pop workreg2&lt;br /&gt;
 20c:	1f 91       	pop	r17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 20e:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 210:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zuweisung von Registern zu Parameternummer und die Register für die Rückgabewerte sind in den &amp;quot;Register Usage Guidelines&amp;quot; der avr-libc-Dokumentation erläutert.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/assembler.html avr-libc-Dokumentation: Related Pages/avr-libc and assembler programs]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage avr-libc-Dokumentation: Related Pages/FAQ/&amp;quot;What registers are used by the C compiler?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
== Globale Variablen für Datenaustausch ==&lt;br /&gt;
&lt;br /&gt;
Oftmals kommt man um globale Variablen nicht herum, z.B. um den Datenaustausch zwischen Hauptprogramm und Interrupt-Routinen zu realisieren. &lt;br /&gt;
Hierzu muss man im Assembler wissen, wo genau die Variable vom C-Compiler abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
Hierzu muss die Variable, hier &amp;quot;zaehler&amp;quot; genannt, zuerst im C-Code als Global definiert werden, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
int16_t main (void)&lt;br /&gt;
{&lt;br /&gt;
    // irgendein Code, in dem zaehler benutzt werden kann&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im foldenden Assembler-Beispiel wird der Externe Interrupt0  verwendet, um den Zähler hochzuzählen. Es fehlen die Initialisierungen des Interrupts und die Interrupt-Freigabe, so richtig sinnvoll ist der Code auch nicht, aber er zeigt (hoffentlich) wie es geht.&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit Interrupt-Vektoren gilt beim GCC-Assembler das Gleiche, wie bei C: Man muss die exakte Schreibweise beachten, ansonsten wird nicht der Interrupt-Vektor angelegt, sondern eine neue Funktion - und man wundert sich, dass nichts funktionert (vgl. das AVR-GCC-Handbuch).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
.extern zaehler&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      //; wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     //; Status-Register (SREG) sichern!&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     lds temp,zaehler               //; Wert aus dem Speicher lesen&lt;br /&gt;
     inc temp                       //; bearbeiten&lt;br /&gt;
     sts zaehler,temp               //; und wieder zurückschreiben&lt;br /&gt;
&lt;br /&gt;
     pop temp                       //; die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen im Assemblerfile anlegen ===&lt;br /&gt;
&lt;br /&gt;
Alternativ können Variablen aber auch im Assemblerfile angelegt werden. Dadurch kann auf eine .c-Datei verzichtet werden. Für das obige Beispiel könnte der Quelltext dann die Dateien zaehl_asm.S und zaehl_asm.h abgelegt werden, so dass nur noch zaehl_asm.S mit kompiliert werden müsste.&lt;br /&gt;
&lt;br /&gt;
Anstatt im Assemblerfile über das Schlüsselwort &#039;&#039;.extern &#039;&#039; auf eine vorhandene Variable zu verweisen, wird dazu mit dem Schlüsselwort &#039;&#039;.comm&#039;&#039; die benötigte Anzahl von Bytes für eine Variable reserviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.S&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
//; 1 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 1&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Headerdatei wird dann auf die Variable nur noch verwiesen (Schlüsselwort &#039;&#039;extern&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef ZAEHL_ASM_H&lt;br /&gt;
#define ZAEHL_ASM_H&lt;br /&gt;
&lt;br /&gt;
extern volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu globalen Variablen in C werden so angelegte Variablen nicht automatisch mit dem Wert 0 initialisiert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer als 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Variablen, die größer als &#039;&#039;&#039;ein&#039;&#039;&#039; Byte sind, können in Assembler auf ähnliche Art angesprochen werden. Hierzu müssen nur genug Bytes angefordert werden, um die Variable aufzunehmen. Soll z.B. für den Zähler eine Variable vom Typ &#039;&#039;unsigned long&#039;&#039;, also &#039;&#039;uint32_t&#039;&#039; verwendet werden, so müssen 4 Bytes reserviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die dazugehörige Deklaration im Headerfile wäre dann:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
extern volatile uint32_t zaehler;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen, die größer als ein Byte sind, werden die Werte beginnend mit dem niederwertigsten Byte im RAM abgelegt. Das folgende Codeschnippsel zeigt, wie unter Assembler auf die einzelnen Bytes zugegriffen werden kann. Dazu wird im Interrupt nun ein 32-Bit Zähler erhöht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      // wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     // Status-Register (SREG) sichern !&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     // 32-Bit-Zähler incrementieren&lt;br /&gt;
     lds temp, (zaehler + 0)        // 0. Byte (niederwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 0), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
     lds temp, (zaehler + 1)        // 1. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 1), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 2)        // 2. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 2), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 3)        // 3. Byte (höchstwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 3), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
RAUS:&lt;br /&gt;
     pop temp                       // die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TODO:&#039;&#039;&#039; 16-Bit / 32-Bit Variablen, Zugriff auf Arrays (Strings)&lt;br /&gt;
&lt;br /&gt;
= Anhang =&lt;br /&gt;
&lt;br /&gt;
== Besonderheiten bei der Anpassung bestehenden Quellcodes ==&lt;br /&gt;
&lt;br /&gt;
Einige Funktionen, die in frühren Versionen der avr-libc vorhanden waren, werden inzwischen als veraltet angesehen. Sie sind nicht mehr vorhanden oder als &#039;&#039;deprecated&#039;&#039; (missbilligt) ausgewiesen und Definitionen in &amp;lt;compat/deprecated.h&amp;gt; verschoben. Es empfiehlt sich, vorhandenen Code zu portieren und die alten Funktionen nicht mehr zu nutzen, auch wenn diese noch zur Verfügung stehen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zur Deklaration von Interrupt-Routinen ===&lt;br /&gt;
&lt;br /&gt;
Die Funktionen (eigentlich Makros) &#039;&#039;SIGNAL&#039;&#039; und &#039;&#039;INTERRUPT&#039;&#039; zur Deklaration von Interruptroutinen sollten nicht mehr genutzt werden. &lt;br /&gt;
&lt;br /&gt;
In aktuellen Versionen der avr-libc (z.B. avr-libc 1.4.3 aus WinAVR 20060125) werden Interruptroutinen, die &#039;&#039;&#039;nicht&#039;&#039;&#039; durch andere Interrupts &#039;&#039;&#039;unterbrechbar&#039;&#039;&#039; sind, mit ISR deklariert (siehe Abschnitt im Hauptteil). Auch die Benennung wurden vereinheitlicht und an die üblichen Bezeichnungen in den AVR Datenblättern angepasst. In der Dokumentation der avr-libc sind alte und neue Bezeichnungen in der Tabelle gegenübergestellt. Die erforderlichen Schritte zur Portierung:&lt;br /&gt;
&lt;br /&gt;
* #include von avr/signal.h entfernen&lt;br /&gt;
* SIGNAL duch ISR ersetzen&lt;br /&gt;
* Name des Interrupt-Vektors anpassen (SIG_* durch entsprechendes *_vect)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für die Anpassung zuerst ein &amp;quot;alter&amp;quot; Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer2 Output Compare bei einem ATmega8 */&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE2)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt wird der Vektor mit TIMER2 COMP bezeichnet. Die Bezeichnung in der avr-libc entspricht dem Namen im Datenblatt, Leerzeichen werden durch Unterstriche (_) ersetzt und ein _vect angehängt. &lt;br /&gt;
&lt;br /&gt;
Der neue Code sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt; &lt;br /&gt;
/* signal.h entfällt */&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER2_COMP_vect)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Unklarheiten bezüglich der neuen Vektorlabels hilft (noch) ein Blick in die Headerdatei des entsprechenden Controllers. Für das vorherige Beispiel also der Blick in die Datei iom8.h für den ATmega8, dort findet man die veraltete Bezeichnung unterhalb der aktuellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect		_VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2		_VECTOR(3)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Nachfolgendes mit avr-libc 1.4.5 (in WinAVR 1/2007 behoben - noch eine Weile auskommentiert lassen und dann löschen.&lt;br /&gt;
Konnte in alten Versionen signal.h ohne interrupt.h eingebunden werden, erhält man bei Verwendung der avr-libc Version 1.4.3 (WinAVR 2/2005) beim Compilieren eine Fehlermeldung, da mit signal.h nicht die erforderlichen Definitionen eingebunden werden. Der Lösungsvorschlag in signal.h auch interrupt.h einzubinden, wurde von den avr-libc-Enwicklern akzeptiert und das Problem ist  im Quellcode (CVS) bereits behoben. Es ist aber noch keine avr-libc-&amp;quot;Release&amp;quot; bzw. noch kein WinAVR mit dieser avr-libc-Korrektur verfügbar (Stand 5.2.2006). Will oder kann man den Quellcode nicht aktualisieren, gibt es folgende Alternativen:&lt;br /&gt;
* in Quellcodedateien, in denen nur avr/signal.h eingebunden wird, interrupt.h einbinden (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;). signal.h weiterhin einbinden, falls Kompatibiltät mit alten Versionen gewünscht ist.&lt;br /&gt;
* in der Datei signal.h (bein WinAVR in c:/WinAVR/avr/include/avr/signal.h) ein (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;) ergänzen --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;unterbrechbare&#039;&#039;&#039; Interruptroutinen, die mittels &#039;&#039;INTERRUPT&#039;&#039; deklariert sind, gibt es keinen direkten Ersatz in Form eines Makros. Solche Routinen sind laut Dokumentation der avr-libc in folgender Form zu deklarieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* ** alt ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
INTERRUPT(SIG_OVERFLOW0)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ** neu: ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void TIMER0_OVF_vect(void) __attribute__((interrupt));&lt;br /&gt;
void TIMER0_OVF_vect(void) &lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von &#039;&#039;INTERRUPT&#039;&#039; die Header-Datei &#039;&#039;compat/deprecated.h&#039;&#039; einzubinden. Man sollte bei dieser Gelegenheit jedoch nochmals überprüfen, ob die Funktionalität von &#039;&#039;INTERRUPT&#039;&#039; tatsächlich gewollt ist. In vielen Fällen wurde &#039;&#039;INTERRUPT&#039;&#039; dort genutzt, wo eigentlich &#039;&#039;SIGNAL&#039;&#039; (nunmehr &#039;&#039;ISR&#039;&#039;) hätte genutzt werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;inp&#039;&#039; und &#039;&#039;outp&#039;&#039; zum Einlesen bzw. Schreiben von Registern sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
unsigned char i, j;&lt;br /&gt;
&lt;br /&gt;
// alt:&lt;br /&gt;
  i = inp(PINA);&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  outp(PORTB, j);&lt;br /&gt;
&lt;br /&gt;
// neu (nicht mehr wirklich neu...):&lt;br /&gt;
  i = PINA&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  PORTB = j;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von inp und outp die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Zugriff auf Bits in Registern ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;cbi&#039;&#039; und &#039;&#039;sbi&#039;&#039; zum Löschen und Setzen von Bits sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg. Die Bezeichnung ist ohnehin irreführend da die Funktionen nur für Register mit Adressen im unteren Speicherbereich tatsächlich in die Assembleranweisungen cbi und sbi übersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// alt:&lt;br /&gt;
  sbi(PORTB, PB2);&lt;br /&gt;
  cbi(PORTC, PC1);&lt;br /&gt;
&lt;br /&gt;
// neu (auch nicht mehr wirklich neu...):&lt;br /&gt;
  PORTB |=  (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
  PORTC &amp;amp;= ~(1&amp;lt;&amp;lt;PC1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von sbi und cbi die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden. Wer unbedingt will, kann sich natürlich eigene Makros mit aussagekräftigeren Namen definieren. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &amp;amp;= ~(1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Selbstdefinierte (nicht-standardisierte) ganzzahlige Datentypen ===&lt;br /&gt;
&lt;br /&gt;
Bei den im Folgenden genannten Typdefinitionen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich, bei der Überarbeitung von altem Code die im Abschnitt &#039;&#039;standardisierten ganzzahligen Datentypen&#039;&#039; beschriebenen Datentypen zu nutzen (stdint.h) und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef unsigned char      BYTE;       // besser: uint8_t  aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned short     WORD;       // besser: uint16_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long      DWORD;      // besser: uint32_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long long QWORD;      // besser: uint64_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; BYTE : Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 255. &lt;br /&gt;
&lt;br /&gt;
; WORD : Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 65535. &lt;br /&gt;
&lt;br /&gt;
; DWORD : Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
; QWORD : Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Funktionen im Makefile ==&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken (Libraries/.a-Dateien) hinzufügen ===&lt;br /&gt;
&lt;br /&gt;
Um Funktionen aus Bibliotheken (&amp;quot;echte&amp;quot; Libraries, *.a-Dateien) zu nutzen, sind dem Linker die Namen der Bibliotheken als Parameter zu übergeben. Dazu ist die Option -l (kleines L) vorgesehen, an die der Name der Library angehängt wird. &lt;br /&gt;
&lt;br /&gt;
Dabei ist zu beachten, dass der Name der Library und der Dateiname der Library nicht identisch sind. Der hinter -l angegebene Name entspricht dem Dateinamen der Library ohne die Zeichenfolge &#039;&#039;lib&#039;&#039; am Anfang des Dateinamens und ohne die Endung &#039;&#039;.a&#039;&#039;. Sollen z.B. Funktionen aus einer Library mit dem Dateinamen &#039;&#039;libefsl.a&#039;&#039; eingebunden (gelinkt) werden, lautet der entsprechende Parameter -lefsl (vergl. auch -lm zum Anbinden von libm.a). &lt;br /&gt;
&lt;br /&gt;
In Makefiles wird traditonell eine make-Variable LDLIBS genutzt, in die &amp;quot;l-Parameter&amp;quot; abgelegt werden. Die WinAVR-makefile-Vorlage enthält diese Variable zwar nicht, dies stellt jedoch keine Einschränkung dar, da alle in der make-Variable LDFLAGS abgelegten Parameter an den Linker weitergereicht werden. &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Einbinden von Funktionen aus einer Library efsl (Dateiname libefsl.a)&lt;br /&gt;
LDFLAGS += -lefsl&lt;br /&gt;
# Einbinden von Funktionen aus einer Library xyz (Dateiname libxyz.a)&lt;br /&gt;
LDFLAGS += -lxyz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Liegen die Library-Dateien nicht im Standard Library-Suchpfad, sind die Pfade mittels Parameter &#039;&#039;-L&#039;&#039; ebenfalls anzugeben. (Der vordefinierte Suchpfad kann mittels &#039;&#039;avr-gcc --print-search-dirs&#039;&#039; angezeigt werden.)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Projekt (&amp;quot;superapp2&amp;quot;), in dem der Quellcode von zwei Libraries (efsl und xyz) und der Quellcode der eigentlichen Anwendung in verschiedenen Verzeichnissen mit der folgenden &amp;quot;Baumstruktur&amp;quot; abgelegt sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
superapp2&lt;br /&gt;
|&lt;br /&gt;
+----- efslsource (darin libefsl.a)&lt;br /&gt;
|&lt;br /&gt;
+----- xyzsource (darin libxyz.a)&lt;br /&gt;
|&lt;br /&gt;
+----- firmware (darin Anwendungs-Quellcode und Makefile)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt, dass im Makefile die Verzeichnis efslsource und xyzsource in den Library-Suchpfad aufzunehmen sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
LDFLAGS += -L../efslsource/ -L../xyzsource/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fuse-Bits ===&lt;br /&gt;
&lt;br /&gt;
Zur Berechnung der Fuse-Bits bietet sich neben dem Studium des Datenblattes auch der [http://palmavr.sourceforge.net/cgi-bin/fc.cgi AVR Fuse Calculator] an. Gewarnt werden muss vor der Benutzung von PonyProg, weil dort durch die negierte Darstellung gern Fehler gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Soll die Programmierung von Fuse- und Lockbits automatisiert werden, kann man dies ebenfalls durch Einträge im Makefile vornehmen, die beim Aufruf von &amp;quot;make program&amp;quot; an die genutzte Programmiersoftware übergeben werden. In der makefile-Vorlage von WinAVR (und mfile) gibt es dafuer jedoch keine &amp;quot;Ausfüllhilfe&amp;quot; (Stand 9/2006). Die folgenden Ausführungen gelten für die Programmiersoftware [[AVRDUDE]] (Standard in der WinAVR-Vorlage), können jedoch sinngemäß auf andere Programmiersoftware übertragen werden, die die Angabe der Fuse- und Lockbits-Einstellungen per Kommandozeilenparameter unterstützt (z.B. stk500.exe). Im einfachsten Fall ergänzt man im Makefile einige Variablen, deren Werte natürlich vom verwendeten Controller und den gewünschten Einstellungen abhängen (vgl. Datenblatt Fuse-/Lockbits):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#---------------- Programming Options (avrdude) ----------------&lt;br /&gt;
&lt;br /&gt;
#...&lt;br /&gt;
#Beispiel! f. ATmega16 - nicht einfach uebernehmen! Zahlenwerte anhand&lt;br /&gt;
#--------- des Datenblatts nachvollziehen und gegebenenfalls aendern.&lt;br /&gt;
#&lt;br /&gt;
AVRDUDE_WRITE_LFUSE = -U lfuse:w:0xff:m&lt;br /&gt;
AVRDUDE_WRITE_HFUSE = -U hfuse:w:0xd8:m&lt;br /&gt;
AVRDUDE_WRITE_LOCK  = -U lock:w:0x2f:m&lt;br /&gt;
#...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit diese Variablen auch genutzt werden, ist der Aufruf von avrdude im Makefile entsprechend zu ergänzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Program the device.  &lt;br /&gt;
program: $(TARGET).hex $(TARGET).eep&lt;br /&gt;
# ohne Fuse-/Lock-Einstellungen (nach WinAVR Vorlage Stand 4/2006)&lt;br /&gt;
#	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
#        $(AVRDUDE_WRITE_EEPROM)&lt;br /&gt;
# mit Fuse-/Lock-Einstellungen&lt;br /&gt;
        $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_LFUSE) \&lt;br /&gt;
        $(AVRDUDE_WRITE_HFUSE) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
        $(AVRDUDE_WRITE_EEPROM) $(AVRDUDE_WRITE_LOCK)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weiter Möglichkeit besteht darin, die Fuse- und Lockbit-Einstellungen vom Preprozessor/Compiler generieren zu lassen. Die Fuse-Bits werden dann bei Verwendung von AVRDUDE in eigene Hex-Files geschrieben. Hierzu kann man z.B. folgendes Konstrukt verwenden:&lt;br /&gt;
&lt;br /&gt;
In eine der C-Sourcen wird eine Variable je Fuse-Byte vom Typ &#039;&#039;unsigned char&#039;&#039; deklariert und in eine extra Section gepackt. Dies kann entweder in einem vorhandenen File passieren oder in ein neues (z.B. fuses.c) geschrieben werden. Das File muss im Makefile aber auf jeden Fall mit kompiliert und gelinkt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// tiny 2313 fuses low byte&lt;br /&gt;
#define CKDIV8  7&lt;br /&gt;
#define CKOUT   6&lt;br /&gt;
#define SUT1    5&lt;br /&gt;
#define SUT0    4&lt;br /&gt;
#define CKSEL3  3&lt;br /&gt;
#define CKSEL2  2&lt;br /&gt;
#define CKSEL1  1&lt;br /&gt;
#define CKSEL0  0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses high byte&lt;br /&gt;
#define DWEN       7&lt;br /&gt;
#define EESAVE     6&lt;br /&gt;
#define SPIEN      5&lt;br /&gt;
#define WDTON      4&lt;br /&gt;
#define BODLEVEL2  3&lt;br /&gt;
#define BODLEVEL1  2&lt;br /&gt;
#define BODLEVEL0  1&lt;br /&gt;
#define RSTDISBL   0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses extended byte&lt;br /&gt;
#define SELFPRGEN  0&lt;br /&gt;
&lt;br /&gt;
#define LFUSE         __attribute__ ((section (&amp;quot;lfuses&amp;quot;)))&lt;br /&gt;
#define HFUSE         __attribute__ ((section (&amp;quot;hfuses&amp;quot;)))&lt;br /&gt;
#define EFUSE         __attribute__ ((section (&amp;quot;efuses&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// select ext crystal 3-8Mhz&lt;br /&gt;
unsigned char lfuse LFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;CKDIV8) | (1&amp;lt;&amp;lt;CKOUT) | (1&amp;lt;&amp;lt;CKSEL3) | (1&amp;lt;&amp;lt;CKSEL2) | &lt;br /&gt;
      (0&amp;lt;&amp;lt;CKSEL1) | (1&amp;lt;&amp;lt;CKSEL0) | (0&amp;lt;&amp;lt;SUT1) | (1&amp;lt;&amp;lt;SUT0) );&lt;br /&gt;
unsigned char hfuse HFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;DWEN) | (1&amp;lt;&amp;lt;EESAVE) | (0&amp;lt;&amp;lt;SPIEN) | (1&amp;lt;&amp;lt;WDTON) | &lt;br /&gt;
      (1&amp;lt;&amp;lt;BODLEVEL2) | (1&amp;lt;&amp;lt;BODLEVEL1) | (0&amp;lt;&amp;lt;BODLEVEL0) | (1&amp;lt;&amp;lt;RSTDISBL) );&lt;br /&gt;
unsigned char efuse EFUSE =&lt;br /&gt;
    ((0&amp;lt;&amp;lt;SELFPRGEN));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG: Die Bitpositionen wurden nicht vollständig getestet!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Eine &amp;quot;1&amp;quot; bedeutet hier, dass das Fuse-Bit &#039;&#039;nicht&#039;&#039; programmiert wird - die Funktion also i.A. nicht aktiviert ist. Eine &amp;quot;0&amp;quot; hingegen aktiviert die meisten Funktionen. Dies ist wie im Datenblatt (1 = unprogrammed, 0 = programmed).&lt;br /&gt;
&lt;br /&gt;
Das Makefile muss nun noch um folgende Targets erweitert werden (mit Tabulator einrücken - nicht mit Leerzeichen):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
lfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j lfuses --change-section-address lfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-lfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-lfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U lfuse:w:$(TARGET)-lfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
hfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j hfuses --change-section-address hfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-hfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-hfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U hfuse:w:$(TARGET)-hfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
efuses: build&lt;br /&gt;
        -$(OBJCOPY) -j efuses --change-section-address efuses=0 \&lt;br /&gt;
         -O ihex $(TARGET).elf $(TARGET)-efuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-efuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U efuse:w:$(TARGET)-efuse.hex;&lt;br /&gt;
        fi;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Target &amp;quot;clean&amp;quot; muss noch um die Zeilen&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
        $(REMOVE) $(TARGET)-lfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-hfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-efuse.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
erweitert werden, wenn auch die Fuse-Dateien gelöscht werden sollen.&lt;br /&gt;
&lt;br /&gt;
Um nun die Fusebits des angeschlossenen Controllers zu programmieren muss lediglichein &amp;quot;make lfuses&amp;quot;, &amp;quot;make hfuses&amp;quot; oder &amp;quot;make efuses&amp;quot; gestartet werden.&lt;br /&gt;
Bei den Fuse-Bits ist besondere Vorsicht geboten, da diese das Programmieren des Controllers unmöglich machen können. Also erst programmieren, wenn man einen HV-Programmierer hat oder ein paar Reserve-AVRs zur Hand ;-)&lt;br /&gt;
&lt;br /&gt;
Um weiterhin den &amp;quot;normalen&amp;quot; Flash beschreiben zu können, ist es wichtig, für das Target &amp;quot;*.hex&amp;quot; im Makefile nicht nur &amp;quot;-R .eeprom&amp;quot; als Parameter zu übergeben sondern zusätzlich noch &amp;quot;-R lfuses -R efuses -R hfuses&amp;quot;. Sonst bekommt AVRDUDE Probleme diese Sections in den Flash (wo sie ja nicht hingehören) zu schreiben.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[AVR_Fuses#Vergleich_der_Fuses_bei_verschiedenen_Programmen|Vergleich der Fuses bei verschiedenen Programmen]]&lt;br /&gt;
&lt;br /&gt;
== Externe Referenzspannung des internen Analog-Digital-Wandlers ==&lt;br /&gt;
&lt;br /&gt;
Die minimale (externe) Referenzspannung des ADC darf nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. z.B. beim ATMEGA8 darf sie laut Datenblatt (S.245, Tabelle 103, Zeile &amp;quot;VREF&amp;quot;) 2,0V nicht unterschreiten. HINWEIS: diese Information findet sich erst in der letzten Revision (Rev. 2486O-10/04) des Datenblatts.&lt;br /&gt;
&lt;br /&gt;
Meiner &amp;lt;!-- Wer? - es gibt inzwischen x Leute die mehr oder weniger viel in diesem Artikel geschrieben haben --&amp;gt; eigenen Erfahrung nach kann man aber (auf eigene Gefahr und natürlich nicht für Seriengeräte) durchaus noch ein klein wenig weiter heruntergehen, bei dem von mir unter die Lupe genommenen ATMEGA8L (also die Low-Voltage-Variante) funktioniert der ADC bei 5V Betriebsspannung mit bis zu VREF=1,15V hinunter korrekt, ab 1,1V und darunter digitalisiert er jedoch nur noch Blödsinn). Ich würde sicherheitshalber nicht unter 1,5V gehen und bei niedrigeren Betriebsspannungen mag sich die Untergrenze für VREF am Pin AREF ggf. nach oben&#039;&#039;&#039;(!)&#039;&#039;&#039; verschieben.&lt;br /&gt;
&lt;br /&gt;
In der letzten Revision des Datenblatts ist außerdem korrigiert, dass ADC4 und ADC5 sehr wohl 10 Bit Genauigkeit bieten (und nicht bloß 8 Bit, wie in älteren Revisionen irrtümlich angegeben.)&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* stdio.h, malloc() &lt;br /&gt;
* Code-Optimierungen (&amp;quot;tricks&amp;quot;), siehe auch Application Note [http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf AVR035: Efficient C Coding for AVR]&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
* SPI siehe [http://www.uni-koblenz.de/~physik/informatik/MCU/SPI.pdf SPI Bus mit Atmel AVR]&lt;br /&gt;
* I²C / TWI Bus [http://www.roboternetz.de/wissen/index.php/TWI]&lt;br /&gt;
* Bootloader (bez. auf boot.h)&lt;br /&gt;
* CAN-Bus&lt;br /&gt;
* Einsatz von einfachen Betriebssystemen auf dem AVR&lt;br /&gt;
* Übersicht zu den C bzw. GCC-predefined Makros (__DATE__, __TIME__,...)&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
* ADC ; &lt;br /&gt;
* Timer&lt;br /&gt;
* USB ; Steuerung mit USB&lt;br /&gt;
* Multiplexen Siebensegment&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28253</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28253"/>
		<updated>2008-05-27T17:03:00Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Exkurs: Makefiles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[AVR-GCC]] erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Progammiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong]. Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern, weder in Assembler noch in einer anderen Sprache. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR Controllern finden. Bei WinAVR gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden stetig weiterentwickelt. Erläuterungen und Beispiele beziehen sich auf den C-Compiler avr-gcc ab Version 3.4 und die avr-libc ab Version 1.4.3. Die Unterschiede zu älteren Versionen werden im Haupttext und Anhang zwar erläutert, Anfängern sei jedoch empfohlen, die aktuellen Versionen zu nutzen (für MS-Windows: aktuelle Version des [[WinAVR]]-Pakets). &lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in PDF-Form erhältlich bei:&lt;br /&gt;
http://www.siwawi.arubi.uni-kl.de/avr_projects/AVR-GCC-Tutorial_-_www_mikrocontroller_net.pdf&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels avr-gcc/avr-libc zu erstellen und zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR Controllers, der vom avr-gcc Compiler unterstützt wird (alle ATmegas und die meisten AT90, siehe Dokumentation der avr-libc für unterstützte Typen). Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man [[AVR#Starterkits|hier]].&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc Compiler und die avr-libc. Kostenlos erhältlich für nahezu alle Plattformen und Betriebssysteme. Für MS-Windows im Paket [[WinAVR]]; für Unix/Linux siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]]).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder nur die eigenen C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
&lt;br /&gt;
* Die [[AVR Checkliste]] durcharbeiten.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/2 GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  auf den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
&lt;br /&gt;
* Die Beispieleprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweisen sind aber auch auf C-Programme übertragbar.&lt;br /&gt;
&lt;br /&gt;
* Einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien, um das Problem nachzuvollziehen, sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z. B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.&amp;amp;nbsp;B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.&amp;amp;nbsp;B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen, um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu zwei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer integrierten Entwicklungsumgebung (IDE = &#039;&#039;&#039;I&#039;&#039;&#039;ntegrated &#039;&#039;&#039;D&#039;&#039;&#039;evelopment &#039;&#039;&#039;E&#039;&#039;&#039;nvironment), bei der alle Einstellungen z.&amp;amp;nbsp;B. in Dialogboxen durchgeführt werden können. Unter Anderem kann AVRStudio ab Version 4.12 (kostenlos auf [http://www.atmel.com/ atmel.com]) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs für den avr-gcc (ohne Anspruch auf Vollständigkeit): AtmanAvr C (relativ günstig), KamAVR (kostenlos), VMLab (ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.&amp;amp;nbsp;B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand der universellen und plattformunabhängigen Vorgehensweise mittels make und Makefiles näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind lediglich Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xff;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5a)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/;  // (5b)&lt;br /&gt;
   }                         // (5c)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (6)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In der mit (1) markierten Zeile wird eine so genannte Header-Datei eingebunden. In io.h sind die Registernamen definiert, die im späteren Verlauf genutzt werden.&lt;br /&gt;
&lt;br /&gt;
* Bei (2) beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion main.&lt;br /&gt;
&lt;br /&gt;
* Die Anschlüsse eines AVR (&amp;quot;Beinchen&amp;quot;) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Richtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B zu Ausgängen.&lt;br /&gt;
&lt;br /&gt;
* (4) stellt die Werte der Ausgänge ein. Die den ersten beiden Bits des Ports zugeordneten Anschlüsse (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential).&lt;br /&gt;
&lt;br /&gt;
* (5) ist die so genannte Hauptschleife (main-loop). Dies ist eine Programmschleife, welche kontinuierlich wiederkehrende Befehle enthält. In diesem Beispiel ist sie leer. Der Controller durchläuft die Schleife immer wieder, ohne dass etwas passiert (außer das Strom verbraucht wird). Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Ohne diese Schleife wäre der Zustand des Controllers nach dem Programmende undefiniert.&lt;br /&gt;
&lt;br /&gt;
* (6) wäre das Programmende. Die Zeile ist nur aus Gründen der C-Kompatibilität enthalten: int main(void) besagt, dass die Funktion einen Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein auf dem Controller lauffähiges Programm zu übersetzen, wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen Makefile (ohne Endung) im selben Verzeichnis, in dem auch die Datei main.c mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im folgenden Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\tmp\gcc_tut\quickstart&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei main.hex, in der der Code für den AVR enthalten ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.&amp;amp;nbsp;B. über In-System-Programming (ISP) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann (z.&amp;amp;nbsp;B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio), kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine LED leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode mit einem dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Exkurs: Makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebungen à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Plattformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt, genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.com) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das Makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den im Makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im Makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten Makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt [[AVR-GCC-Tutorial#EEPROM|Speicherzugriffe]]), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendeten Controllers gesetzt. Eine Liste der von avr-gcc und der avr-libc unterstützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien eintragen ==&lt;br /&gt;
&lt;br /&gt;
Der Name der Quellcodedatei, welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.&amp;amp;nbsp;B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
# nicht auskommentiert: EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Das erstellte Makefile und der Code müssen im gleichen Ordner sein, auch sollte der Dateiname nicht verändert werden. &lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem Makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschinencode erzeugt. Beim Update auf eine neue Compilerversion ist zu beachten, dass diese möglicherweise intern andere Optimierungsalgorithmen verwendet und sich dadurch die Größe des Machinencodes etwas ändert, ohne dass man im Quellcode etwas geändert hat.&lt;br /&gt;
&lt;br /&gt;
Als Orientierungswerte die Größe des Maschinencodes bei verschiedenen Optionen für einen nicht näher spezifizierten relativ kleinen Testcode bei Verwendung einer nicht näher spezifizierten Compilerversion. &lt;br /&gt;
&lt;br /&gt;
* -O0 : 12&#039;217 Byte&lt;br /&gt;
&lt;br /&gt;
* -O1 : 9&#039;128 Byte&lt;br /&gt;
&lt;br /&gt;
* -O2 : 1&#039;670 Byte&lt;br /&gt;
&lt;br /&gt;
* -O3 : 3&#039;004 Byte&lt;br /&gt;
&lt;br /&gt;
* -Os : 1&#039;695 Byte&lt;br /&gt;
&lt;br /&gt;
Im diesem Testfall führt die Option -O2 mit zum kompaktesten Code, dies  allerdings hier nur mit 25 Bytes &amp;quot;Vorsprung&amp;quot;. Es kann durchaus sein, dass nur wenige Programmerweiterungen dazu führen, dass Compilieren mit -Os wieder in kompakteren Code resultiert.&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/using_tools.html#gcc_optO avr-libc manual Abschnitt Using the gnu-tools/Compiler-Optionen]&lt;br /&gt;
&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_optflags avr-libc Manual FAQ Nr. 16] (Stand avr-libc Version 1.4.5)&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wird hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (großes S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten, lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Taktfrequenz ===&lt;br /&gt;
&lt;br /&gt;
Neuere Versionen der WinAVR/Mfile Vorlage für Makefiles beinhalten die Definition einer Variablen F_CPU (WinAVR 2/2005). Darin wird die Taktfrequenz des Controllers in Hertz eingetragen. Die Definition steht dann im gesamten Projekt ebenfalls unter der Bezeichnung F_CPU zur Verfügung (z.&amp;amp;nbsp;B. um daraus UART-, SPI- oder ADC-Frequenzeinstellungen abzuleiten).&lt;br /&gt;
&lt;br /&gt;
Die Angabe hat rein &amp;quot;informativen&amp;quot; Charakter, die tatsächliche Taktrate wird über den externen Takt (z.&amp;amp;nbsp;B. Quarz) bzw. die Einstellung des internen R/C-Oszillators  bestimmt. Die Nutzung von F_CPU hat also nur Sinn, wenn die Angabe mit dem tatsächlichen Takt übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Innerhalb neuerer Versionen der avr-libc (ab Version 1.2) wird die Definition der Taktfrequenz (F_CPU) zur Berechnung der Wartefunktionen in delay.h genutzt. Diese funktionieren nur dann korrekt, wenn F_CPU mit der tatsächlichen Taktfrequenz übereinstimmt.&lt;br /&gt;
F_CPU muss dazu jedoch nicht unbedingt im makefile definiert werden. Es reicht aus, wird aber bei mehrfacher Anwendung unübersichtlich, vor &#039;&#039;#include &amp;lt;util/delay.h&amp;gt;&#039;&#039; (veraltet: &#039;&#039;#include &amp;lt;avr/delay.h&amp;gt;&#039;&#039;) ein &#039;&#039;#define F_CPU [hier Takt in Hz]UL&#039;&#039; einzufügen. Bei Nutzung von delay.h ist darauf zu achten, dass die Optimierung des Compilers nicht ausgeschaltet ist, sonst wird sehr viel Code erzeugt und die Wartezeit stimmt nicht mit der gewünschten Zeitspanne überein. Vgl. dazu den [http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html entsprechenden Abschnitt der Dokumentation].&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage so genannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* Im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
* Die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* Weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff: (sollte nur noch in Ausnahmefällen genutzt werden)&lt;br /&gt;
&lt;br /&gt;
* Im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
* Die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* Weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler diese &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, einem Nachbau davon (BootICE, Evertool o.&amp;amp;nbsp;ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
Die Verwendung von Makefiles bietet noch viele weitere Möglichkeiten, einige davon werden im Anhang [[AVR-GCC-Tutorial#Zus.C3.A4tzliche_Funktionen_im_Makefile|Zusätzliche Funktionen im Makefile]] erläutert.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige (Integer) Datentypen =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei stdint.h definiert. &lt;br /&gt;
Zur Nutzung der standardisierten Typen bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// ab avr-libc Version 1.2.0 möglich und empfohlen:&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// veraltet: #include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef signed char        int8_t;&lt;br /&gt;
typedef unsigned char      uint8_t;&lt;br /&gt;
&lt;br /&gt;
typedef short              int16_t;&lt;br /&gt;
typedef unsigned short     uint16_t;&lt;br /&gt;
&lt;br /&gt;
typedef long               int32_t;&lt;br /&gt;
typedef unsigned long      uint32_t;&lt;br /&gt;
&lt;br /&gt;
typedef long long          int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* int8_t steht für einen 8-Bit Integer mit einem Wertebereich -128 bis +127.&lt;br /&gt;
* uint8_t steht für einen 8-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 255&lt;br /&gt;
&lt;br /&gt;
* int16_t steht für einen 16-Bit Integer mit einem Wertebereich -32768 bis +32767.&lt;br /&gt;
* uint16_t steht für einen 16-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 65535.&lt;br /&gt;
&lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; werden vorzeichenbehaftete Zahlen abgespeichert. Typen mit vorgestelltem &#039;&#039;u&#039;&#039; dienen der Ablage von postiven Zahlen (inkl. 0).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/(Standard) Integer Types&lt;br /&gt;
&lt;br /&gt;
= Bitfelder =&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&lt;br /&gt;
&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in eine einzelne Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitfelder sparen Platz im RAM, zu Lasten von Platz im Flash, verschlechtern aber unter Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.&lt;br /&gt;
&lt;br /&gt;
Wenn man nur eine paar wenige Variablen vom Typ bool verwenden möchte, kann man&lt;br /&gt;
auch die Headerdatei &amp;lt;stdbool.h&amp;gt; einbinden und sich dann wie gewohnt einen Booltyp anlegen. Variablen dieses Typs brauchen dennoch 1 Byte Speicher, ermöglichen aber eine genaue Unterscheidung zwischen Zahlenvariable und boolscher Variable.&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;C-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum können für allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden&lt;br /&gt;
AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039;, wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output).  Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt.&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend&lt;br /&gt;
angepasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O-Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek (avr-libc) stellt auch Funktionen zur Abfrage eines einzelnen Bits eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist, wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind &#039;&#039;nicht erforderlich&#039;&#039;, man kann auch &amp;quot;einfache&amp;quot; C-Syntax verwenden, die universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) wenn das Bit gesetzt und 0 (&amp;quot;falsch&amp;quot;) wenn es nicht gesetzt ist. Die Negierung des Ausdrucks, also  &#039;&#039;&#039;!&#039;&#039;&#039;(Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)), entspricht &#039;&#039;bit_is_clear&#039;&#039; und gibt einen Wert &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) zurück, falls das Bit nicht gesetzt ist,&lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O-Register einfach wie eine Variable setzen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und outp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben von Bits ====&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausdruck:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit (für 1) eines Bytes hat die Bitnummer 0, das &amp;quot;höchstwertige&amp;quot; (für 128) die Nummer 7.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );    /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3);   /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die warten, bis ein bestimmter Zustand auf einem Bit erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend gewartet&amp;quot; wird. D.h., der Programmablauf bleibt an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den Watchdog ein, muss man darauf achten, dass dieser auch noch getriggert wird (zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Änderung: Habe PINx und PORTx vertauscht.PINx ist für Pull up und PORTx für high bzw low. Michael.L  - &lt;br /&gt;
&lt;br /&gt;
Was sollte diese Aenderung?? Den Rest des Abschnitts auch gelesen? Evtl. die hier nicht erlaeuterten Zusatzfunktion der neuen AVRs durcheinandergeworfen? Aenderung verworfen bis mehr Details.  m.thomas. &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
// Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
&lt;br /&gt;
DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
/* mehr Tipparbeit aber übersichtlicher: */&lt;br /&gt;
DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&lt;br /&gt;
/* Einige Compiler erlauben die Eingabe von Konstanten im &lt;br /&gt;
   Binärformat, z.B. gcc in WinAVR, NICHT aber aus&lt;br /&gt;
   GNU-gcc-Quellen selbst erstellte Compiler. Diese Schreib-&lt;br /&gt;
   weise also nicht nutzen, wenn Code mit anderen &lt;br /&gt;
   ausgetauscht werden soll. */&lt;br /&gt;
DDRB = 0b00011111;    /* direkte Zuweisung &lt;br /&gt;
                         - übersichtlicher &lt;br /&gt;
                         - nicht standardkonform &lt;br /&gt;
                         - selten portabel&lt;br /&gt;
                         - nur bei modifiziertem GCC */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
Weitere Beispiele:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
DDRB = 0xff; &lt;br /&gt;
// Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; DDB0 );&lt;br /&gt;
// Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( ( 1 &amp;lt;&amp;lt; DDB3 ) | ( 1&amp;lt;&amp;lt;DDB4) );&lt;br /&gt;
// Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB |= ( 1 &amp;lt;&amp;lt; DDB0) | ( 1 &amp;lt;&amp;lt; DDB3 );&lt;br /&gt;
// Alle Pins auf Eingang:&lt;br /&gt;
DDRB = 0x00;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&#039;&#039;&lt;br /&gt;
* &#039;&#039;zuerst die Bits im PORTx-Register setzen und&#039;&#039;&lt;br /&gt;
* &#039;&#039;anschließend die Datenrichtung auf Ausgang stellen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Daraus ergibt sich die Abfolge für einen Eingangspin:&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze PORTx: interner Pull-Up aktiv&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze DDRx: Ausgang &amp;quot;high&amp;quot;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bei der Reihenfolge erst DDRx und dann PORTx, kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;&#039;nicht&#039;&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, &#039;&#039;&#039;sondern&#039;&#039;&#039; Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.&amp;lt;/font&amp;gt; (Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände.)&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch.  Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVRs haben sogar an den meisten Pins softwaremäßig zuschaltbare interne Pull-Up Widerstände, welche wir natürlich auch verwenden können.&lt;br /&gt;
&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;DDC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== (Tasten-)Entprellung ====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim Schließen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&lt;br /&gt;
&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen als mehrfache Impulse gezählt wird. Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* bei alter avr-libc: #include &amp;lt;avr/delay.h&amp;gt; */      &lt;br /&gt;
&lt;br /&gt;
/* Einfache Funktion zum Entprellen eines Tasters */&lt;br /&gt;
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)&lt;br /&gt;
{&lt;br /&gt;
    if ( ! (*port &amp;amp; (1 &amp;lt;&amp;lt; pin)) )&lt;br /&gt;
    {&lt;br /&gt;
        /* Pin wurde auf Masse gezogen, 100ms warten   */&lt;br /&gt;
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz&lt;br /&gt;
        _delay_ms(50); &lt;br /&gt;
        if ( *port &amp;amp; (1 &amp;lt;&amp;lt; pin) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Anwender Zeit zum Loslassen des Tasters geben */&lt;br /&gt;
            _delay_ms(50);&lt;br /&gt;
            _delay_ms(50); &lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; PB0 );                 /* PIN PB0 auf Eingang (Taster)            */&lt;br /&gt;
    PORTB |= ( 1 &amp;lt;&amp;lt; PB0 );                 /* Pullup-Widerstand aktivieren            */&lt;br /&gt;
    ...&lt;br /&gt;
    if (debounce(&amp;amp;PINB, PB0))             /* Falls Taster an PIN PB0 gedrueckt..    */&lt;br /&gt;
        PORTD = PIND ^ ( 1 &amp;lt;&amp;lt; PD7 );  /* ..LED an Port PD7 an-&lt;br /&gt;
                                   bzw. ausschalten */&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesem Beispiel ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritische Anwendungen sollte man ein anderes Verfahren nutzen (z.B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).&lt;br /&gt;
&lt;br /&gt;
Zum Thema Entprellen siehe auch:&lt;br /&gt;
* Artikel [[Entprellung]]&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls ben&amp;amp;ouml;tigt, kann eine 16-Bit Variable auch recht einfach manuell in ihre zwei 8-Bit Bestandteile zerlegt werden. Folgendes Beispiel demonstriert dies anhand des pseudo- 16-Bit Registers UBRR.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
typedef union {&lt;br /&gt;
        uint16_t i16;&lt;br /&gt;
        struct {&lt;br /&gt;
                uint8_t i8l;&lt;br /&gt;
                uint8_t i8h;&lt;br /&gt;
        };&lt;br /&gt;
} convert16to8;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
convert16to8 baud;&lt;br /&gt;
baud.i16 = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
UBRRH = baud.i8h;&lt;br /&gt;
UBRRL = baud.i8l;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/*alternativ:*/&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint16_t wFoo16;&lt;br /&gt;
uint8_t bFooLow, bFooHigh;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
wFoo16   = 0xAA55;                 /* zu &amp;quot;zerlegende&amp;quot; 16Bit-Integer */&lt;br /&gt;
bFooHigh = (uint8_t)(wFoo16 &amp;gt;&amp;gt; 8); /* MS-Byte */&lt;br /&gt;
bFooLow  = (uint8_t)(wFoo16);      /* LS-Byte */&lt;br /&gt;
...&lt;br /&gt;
#define us0(Data) ((unsigned char *)(&amp;amp;Data))&lt;br /&gt;
#define us1(Data) ((unsigned char *)((&amp;amp;Data)+1))&lt;br /&gt;
bFooHigh = us1(wFoo16);&lt;br /&gt;
bFoolow  = us0(wFoo16);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen (z.B. ATmega8) teilen sich UBRRH und UCSRC die gleiche Memory-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL) welches Register tats&amp;amp;auml;chlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und &amp;amp;uuml;bergibt den Wert &#039;&#039;3&#039;&#039; und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und &amp;amp;uuml;bergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Bei einigen 16-bit Registern (insbesondere die der 16-bit Timer) erfolgen Schreibzugriffe über das sogenannte &#039;&#039;temporary Register&#039;&#039;. Die Reihenfolge der Zugriffe bestimmt, wann der Wert tatsächlich ins Register geschrieben wird. Typisch wird erst das High-Byte beschrieben (xxxH) und intern im temporary Register zwischengespeichert. Nachdem das Low-Byte (xxxL) geschrieben wurde, setzt der Controller mit diesem und dem im temporary Register zwischengespeicherten Wert für das High-Byte das 16-bit Register. Dabei ist zu beachten, dass intern nur ein temporary Register verfügbar ist, welches in Interruptroutinen mglw. mit einem anderen Wert überschrieben wird, wenn dort ebenfalls 16-bit Register beschrieben werden. avr-gcc/avr-libc berücksichtigen die korrekte Reihenfolge automatisch, wenn die Register mit ihrem &amp;quot;16-bit Label&amp;quot; (ohne H bzw. L) angesprochen werden, dabei ist der Schutz des temporary Registers vor Überschreiben durch Interruptroutinen dennoch zu beachten (im Zweifel beim Schreibzugriff die Interrupts kurzzeitig global deaktivieren).&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t key_pressed(const volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if ( last_state == ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) ) ) {&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    i = key_pressed( &amp;amp;PINB, PB1 );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Im Übrigen können mit volatile gekennzeichnete Variablen auch als const deklariert werden, um sicherzustellen, dass sie nur noch von der Hardware änderbar sind.&lt;br /&gt;
&lt;br /&gt;
= Der UART =&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Über den [[UART]] kann ein AVR leicht mit einer [[RS-232]]-Schnittstelle eines PC oder sonstiger Geräte mit &amp;quot;serieller Schnittstelle&amp;quot; verbunden werden. &lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungen des UART:&lt;br /&gt;
&lt;br /&gt;
* Debug-Schnittstelle: z.B. zur Anzeige von Zwischenergebnissen (&amp;quot;printf-debugging&amp;quot; - hier besser &amp;quot;UART-debugging&amp;quot;) auf einem PC. Auf dem Rechner reicht dazu ein [[RS-232#Terminalprogramme|Terminalprogramm]] (MS-Windows: Hyperterm oder besser Bray-Terminal, [http://www.mikrocontroller.net/forum/read-8-155472.html#new HTerm]; Unix/Linux z.B. minicom). Ein direkter Anschluss ist aufgrund unterschiedlicher Pegel nicht möglich, jedoch sind entsprechende Schnittstellen-ICs wie z.B. ein MAX232 günstig und leicht zu integrieren. Rechner ohne serielle Schnittstelle können über fertige USB-seriell-Adapter angeschlossen werden. &lt;br /&gt;
* &amp;quot;Mensch-Maschine Schnittstelle&amp;quot;: z.B. Konfiguration und Statusabfrage über eine &amp;quot;Kommandozeile&amp;quot; oder Menüs (siehe z.B. Forumsbeitrag [http://www.mikrocontroller.net/topic/52985 Auswertung RS232-Befehle]) &lt;br /&gt;
* Übertragen von gespeicherten Werten: z.B. bei einem Datenlogger&lt;br /&gt;
* Anschluss von Geräten mit serieller Schnittstelle (z.B. (Funk-)Modems, Mobiltelefone, Drucker, Sensoren, &amp;quot;intelligente&amp;quot; LC-Displays, GPS-Empfänger). &lt;br /&gt;
* &amp;quot;Feldbusse&amp;quot; auf RS485/RS422-Basis mittels entsprechenden Bustreiberbausteinen (z.B. MAX485)&lt;br /&gt;
* DMX, Midi etc.&lt;br /&gt;
* LIN-Bus (&#039;&#039;&#039;L&#039;&#039;&#039;ocal &#039;&#039;&#039;I&#039;&#039;&#039;nterconnect &#039;&#039;&#039;N&#039;&#039;&#039;etwork): Preiswerte Sensoren/Aktoren in der Automobiltechnik und darüber hinaus&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben ein bis zwei vollduplexfähigen UART (&#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) schon eingebaut (&amp;quot;Hardware-UART&amp;quot;). &lt;br /&gt;
Übrigens: Vollduplex heißt nichts anderes, als dass der Baustein gleichzeitig senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega, ATtiny) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterscheidet sich vom UART hauptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurationsregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit zeigt an, ob der Sendepuffer bereit ist, um ein zu sendendes Zeichen aufzunehmen. Das Bit wird vom AVR gesetzt (1), wenn der Sendepuffer leer ist. Es wird gelöscht (0), wenn ein Zeichen im Sendedatenregister vorhanden ist und noch nicht in das Sendeschieberegister übernommen wurde. Atmel empfiehlt aus Kompatibilitätsgründen mit kommenden µC, UDRE auf 0 zu setzen, wenn das UCSRA Register beschrieben wird.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
\mathrm{UBRR} = \frac{\mathrm{Taktfrequenz}}{\mathrm{Baudrate} \cdot 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS-232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. &lt;br /&gt;
&amp;lt;!-- &amp;quot;Hackerloesung&amp;quot; auskommentiert - nicht so gut in einem &amp;quot;Einsteiger-Tutorial&amp;quot; - mthomas&lt;br /&gt;
Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfiehlt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== UART initialisieren ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben. Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach gewünschter Funktionsweise die benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Da wir vorerst nur senden möchten und noch keine Interrupts auswerten wollen, gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs mit USART haben mehrere Konfigurationsregister und erfordern eine etwas andere Konfiguration. Für einen ATmega16 z.B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                 // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);     // Asynchron 8N1&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist noch das Baudratenregister &#039;&#039;&#039;UBRR&#039;&#039;&#039; einzustellen. Bei neueren AVRs besteht es aus zwei Registern &#039;&#039;&#039;UBRRL&#039;&#039;&#039; und &#039;&#039;&#039;UBRRH&#039;&#039;&#039;. Der Wert dafür ergibt sich aus der angegebenen Formel durch Einsetzen der Taktfrequenz und der gewünschten Übertragungsrate. Das Berechnen der Formel wird dem [[C-Präprozessor|Präprozessor]] überlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann&lt;br /&gt;
   F_CPU im Makefile definiert werden, eine nochmalige Definition&lt;br /&gt;
   hier wuerde zu einer Compilerwarnung fuehren. Daher &amp;quot;Schutz&amp;quot; durch&lt;br /&gt;
   #ifndef/#endif &lt;br /&gt;
&lt;br /&gt;
   Dieser &amp;quot;Schutz&amp;quot; kann zu Debugsessions führen, wenn AVRStudio &lt;br /&gt;
   verwendet wird und dort eine andere, nicht zur Hardware passende &lt;br /&gt;
   Taktrate eingestellt ist: Dann wird die folgende Definition &lt;br /&gt;
   nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) &lt;br /&gt;
   von AVRStudio - daher Ausgabe einer Warnung falls F_CPU&lt;br /&gt;
   noch nicht definiert: */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000&amp;quot;&lt;br /&gt;
#define F_CPU 4000000UL    // Systemtakt in Hz &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600UL          // Baudrate&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) // Fehler in Promille, 1000 = kein Fehler.&lt;br /&gt;
&lt;br /&gt;
#if ((BAUD_ERROR&amp;lt;990) || (BAUD_ERROR&amp;gt;1010))&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;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    UBRR = UBRR_VAL;&lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&amp;lt;!-- mthomas: warum nicht UL?, wird von AVRStudio auch mit UL übergeben --&amp;gt;&lt;br /&gt;
Wieder für den Mega16 mit zwei Registern für die Baudrateneinstellung eine etwas andere Programmierung. Wichtig ist, dass UBRRH &#039;&#039;&#039;vor&#039;&#039;&#039; UBRRL geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmega16 */&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einige AVR (z.B. ATmega169, ATmega48/88/168, AT90CAN jedoch nicht für z.B. ATmega16/32, ATmega128, ATtiny2313) wird durch die Registerdefinitionen der avr-libc (io*.h) auch für Controller mit zwei UBRR-Registern (UBRRL/UBRRH) ein UBRR bzw. UBRR0 als &amp;quot;16-bit-Register&amp;quot; definiert und man kann auch Werte direkt per UBRR = UBRR_VAL zuweisen. Intern werden dann zwei Zuweisungen für UBRRH und UBRRL generiert. Dies ist nicht bei allen Controllern möglich, da die beiden Register nicht bei allen aufeinanderfolgende Addressen aufweisen. Die getrennte Zuweisung an UBRRH und UBRRL wie im Beispiel gezeigt ist universeller und portabler und daher vorzuziehen.&lt;br /&gt;
&lt;br /&gt;
Die Makros sind sehr praktisch, da sie sowohl automatisch den Wert für UBRR als auch den Fehler in der generierten Baudrate berechnen und im Falle einer Überschreitung (+/-1%) einen Fehler und somit Abbruch im Compilerablauf generieren. Damit können viele Probleme mit &amp;quot;UART sendet komische Zeichen&amp;quot; vermieden werden. Ausserdem kann man mühelos die Einstellung an eine neue Taktfrequenz bzw. Baudrate anpassen, ohne selber rechnen oder in Tabellen nachschlagen zu müssen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. Vorher ist zu prüfen, ob das UART-Modul bereit ist, das zu sendende Zeichen entgegenzunehmen. Die Bezeichnungen des/der Statusregisters mit dem Bit UDRE ist abhängig vom Controllertypen (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    // bei AVR mit einem UART (&amp;quot;classic AVR&amp;quot; z.B. AT90S8515)&lt;br /&gt;
    while (!(USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                  /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&lt;br /&gt;
    /** ODER **/&lt;br /&gt;
&lt;br /&gt;
    // bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                    /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe &amp;quot;String senden&amp;quot; wird durch zwei Funktionen abgearbeitet. Die universelle/controllerunabhängige Funktion uart_puts übergibt jeweils ein Zeichen der Zeichenkette an eine Funktion uart_putc, die abhängig von der vorhandenen Hardware implementiert werden muss. In der Funktion zum Senden eines Zeichens ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// putc fuer AVR mit einem UART (z.B. AT90S8515)&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while(!(USR &amp;amp; (1 &amp;lt;&amp;lt; UDRE)))  /* warte, bis UDR bereit */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = c;                     /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** ODER **/&lt;br /&gt;
&lt;br /&gt;
// bei neueren AVRs andere Bezeichnung fuer die Statusregister, hier ATmega16:&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich */&lt;br /&gt;
    {&lt;br /&gt;
    }                             &lt;br /&gt;
&lt;br /&gt;
    UDR = c;                      /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* puts ist unabhaengig vom Controllertyp */&lt;br /&gt;
void uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != &#039;\0&#039; also ungleich dem &amp;quot;String-Endezeichen&amp;quot; */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die in uart_putc verwendeten Schleifen, in denen gewartet wird bis die UART-Hardware zum senden bereit ist, sind insofern etwas kritisch, da während des Sendens eines Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. empfangenen Zeichen/Bytes zwischengespeichert und in Interruptroutinen an die U(S)ART-Hardware weitergegeben bzw. von ihr ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&lt;br /&gt;
&lt;br /&gt;
=== Schreiben von Variableninhalten ===&lt;br /&gt;
&lt;br /&gt;
Sollen Inhalte von Variablen (Ganzzahlen, Fließkomma) in &amp;quot;menschenlesbarer&amp;quot; Form gesendet werden, ist vor dem Transfer eine Umwandlung in Zeichen (&amp;quot;ASCII&amp;quot;) erforderlich. Bei nur einer Ziffer ist diese Umwandlung relativ einfach: man addiert den ASCII-Wert von Null zur Ziffer und kann diesen Wert direkt senden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_putc (s.o.)&lt;br /&gt;
&lt;br /&gt;
// Ausgabe von 0123456789&lt;br /&gt;
char c;&lt;br /&gt;
&lt;br /&gt;
for (uint8_t i=0; i&amp;lt;=9; ++i) {&lt;br /&gt;
    c = i + &#039;0&#039;;&lt;br /&gt;
    uart_putc( c );&lt;br /&gt;
    // verkuerzt: uart_putc( i + &#039;0&#039; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll mehr als eine Ziffer ausgegeben werden, bedient man sich zweckmäßigerweise vorhandener Funktionen zur Umwandlung von Zahlen in Zeichenketten/Strings. Die Funktion der avr-libc zur Umwandlung von vorzeichenbehafteten 16bit-Ganzzahlen (int16_t) in Zeichenketten heißt &#039;&#039;itoa&#039;&#039; (Integer to ASCII). Man muss der Funktion einen Speicherbereich zur Verarbeitung (buffer) mit Platz für alle Ziffern, das String-Endezeichen (&#039;\0&#039;) und evtl. das Vorzeichen bereitstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   char s[7];&lt;br /&gt;
   int16_t i = -12345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   itoa( i, s, 10 ); // 10 fuer radix -&amp;gt; Dezimalsystem&lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // da itoa einen Zeiger auf den Beginn von s zurueckgibt verkuerzt auch:&lt;br /&gt;
   uart_puts( itoa( i, s, 10 ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für vorzeichenlose 16bit-Ganzzahlen (uint16_t) exisitert &#039;&#039;utoa&#039;&#039;. Die Funktionen für 32bit-Ganzzahlen (int32_t und uint32_t) heißen &#039;&#039;ltoa&#039;&#039; bzw. &#039;&#039;ultoa&#039;&#039;. Da 32bit-Ganzzahlen mehr Stellen aufweisen können, ist ein entsprechend größerer Pufferspeicher vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Auch Fließkommazahlen (float/double) können mit breits vorhandenen Funktionen in Zeichenfolgen umgewandelt werden, dazu existieren die Funktionen &#039;&#039;dtostre&#039;&#039; und &#039;&#039;dtostrf&#039;&#039;. dtostre nutzt Exponentialschreibweise (&amp;quot;engineering&amp;quot;-Format). (Hinweis: z.Zt. existiert im avr-gcc kein &amp;quot;echtes&amp;quot; double, intern wird immer mit &amp;quot;einfacher Genauigkeit&amp;quot;, entsprechend float, gerechnet.) &lt;br /&gt;
&lt;br /&gt;
dtostrf und dtostre benötigen die libm.a der avr-libc. Bei Nutzung von Makefiles ist der Parameter -lm in in LDFLAGS anzugeben (Standard in den WinAVR/mfile-Makefilevorlagen). Nutzt man AVRStudio als IDE für den GNU-Compiler (gcc-Plugin) ist die libm.a unter Libaries auszuwählen: Project -&amp;gt; Configurations Options -&amp;gt; Libaries -&amp;gt; libm.a mit dem Pfeil nach rechts einbinden. Siehe auch die [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|FAQ]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
/* lt. avr-libc Dokumentation:&lt;br /&gt;
char* dtostrf(&lt;br /&gt;
  double __val,&lt;br /&gt;
  char   __width,&lt;br /&gt;
  char   __prec,&lt;br /&gt;
  char * __s&lt;br /&gt;
)  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   // Pufferspeicher ausreichend groß&lt;br /&gt;
   // evtl. Vorzeichen + width + Endezeichen:&lt;br /&gt;
   char s[8]; &lt;br /&gt;
   float f = -12.345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   dtostrf( f, 6, 3, s ); &lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // verkürzt: uart_puts( dtostrf( f, 7, 3, s ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfangen ==&lt;br /&gt;
=== einzelne Zeichen empfangen ===&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von Zeichen muss der Empfangsteil des UART bei der Initialisierung aktiviert werden, indem das RXEN-Bit im jeweiligen Konfigurationsregister (UCSRB bzw UCSR0B/UCSR1B) gesetzt wird. Im einfachsten Fall wird solange gewartet, bis ein Zeichen empfangen wurde, dieses steht dann im UART-Datenregister (UDR bzw. UDR0 und UDR1 bei AVRs mit 2 UARTS) zur Verfügung (sogen. &amp;quot;Polling-Betrieb&amp;quot;). Ein Beispiel für den ATmega16:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Zusaetzlich zur Baudrateneinstellung und der weiteren Initialisierung: */&lt;br /&gt;
void Usart_EnableRX()&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= ( 1 &amp;lt;&amp;lt; RXEN );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion blockiert den Programmablauf. Alternativ kann das RXC-Bit in einer Programmschleife abgefragt werden und dann nur bei gesetztem RXC-Bit UDR ausgelesen werden. Eleganter und in den meisten Anwendungsfällen &amp;quot;stabiler&amp;quot; ist die Vorgehensweise, die empfangenen Zeichen in einer Interrupt-Routine einzulesen und zur späteren Verarbeitung in einem Eingangsbuffer (FIFO-Buffer) zwischenzuspeichern. Dazu existieren fertige und gut getestete [[Libraries|Bibliotheken]] &amp;lt;!-- &amp;quot;echte Libraries&amp;quot; (.a) wie im Verweis beschrieben sind hier eigentlich nicht gemeint, verwirrt hier etwas, da AVR-&amp;quot;Libraries&amp;quot; meist per #defines anpassbare Source-Codes sind, vielleicht so: --&amp;gt; und Quellcodekomponenten (z.B. UART-Library von P. Fleury, procyon-avrlib und einige in der &amp;quot;Academy&amp;quot; von avrfreaks.net).&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html Dokumenation der avr-libc/stdlib.h]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_printf Die Nutzung von printf]&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&amp;lt;!-- nimmermehr: * siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (printf etc.) im [[AVR-Tutorial:_UART]]. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: 9bit&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Zeichenketten (Strings) ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Zeichenketten, muß man sich zunächst darüber im klaren sein, daß es ein Kriterium geben muß, an dem der µC erkennen kann, wann ein String zu Ende ist. Sehr oft wird dazu das Zeichen &#039;Return&#039; benutzt, um das Ende eines Strings zu markieren. Dies ist vom Benutzer einfach eingebbar und er ist auch daran gewöhnt, daß er eine Eingabezeile mit einem Druck auf die Return Taste abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Prinzipiell gibt es jedoch keine Einschränkung bezüglich dieses speziellen Zeichens. Es muß nur sichergestellt werden, daß dieses spezielle &#039;Ende eines Strings&#039; - Zeichen nicht mit einem im Text vorkommenden Zeichen verwechselt werden kann. Wenn also im zu übertragenden Text beispielsweise kein &#039;;&#039; vorkommt, dann spricht nichts dagegen, einen String mit einem &#039;;&#039; abschließen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird die durchaus übliche Annahme getroffen, daß eine Stringübertragung identisch ist mit der Übertragung einer Textzeile und daher mit einem Return (&#039;\n&#039;) abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Das Problem der Übertragung eines Strings reduziert sich damit auf die Aufgabenstellung: Empfange und Sammle Zeichen in einem char Array, bis entweder das Array voll ist oder das &#039;String Ende Zeichen&#039; empfangen wurde. Danach wird der empfangene Text noch mit einem &#039;\0&#039; Zeichen abgeschlossen um einen Standard C-String daraus zu machen, mit dem dann weiter gearbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_gets( char* Buffer, uint8_t MaxLen )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t NextChar;&lt;br /&gt;
  uint8_t StringLen = 0;&lt;br /&gt;
&lt;br /&gt;
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen&lt;br /&gt;
&lt;br /&gt;
                                  // Sammle solange Zeichen, bis:&lt;br /&gt;
                                  // * entweder das String Ende Zeichen kam&lt;br /&gt;
                                  // * oder das aufnehmende Array voll ist&lt;br /&gt;
  while( NextChar != &#039;\n&#039; &amp;amp;&amp;amp; StringLen &amp;lt; MaxLen - 1 ) {&lt;br /&gt;
    *Buffer++ = NextChar;&lt;br /&gt;
    StringLen++;&lt;br /&gt;
    NextChar = uart_getc();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
                                  // Noch ein &#039;\0&#039; anhängen um einen Standard&lt;br /&gt;
                                  // C-String daraus zu machen&lt;br /&gt;
  *Buffer = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf ist darauf zu achten, dass das empfangende Array auch mit einer&lt;br /&gt;
vernünftigen Größe definiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
  char Line[40];      // String mit maximal 39 zeichen&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) );&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Interruptbetrieb==&lt;br /&gt;
&lt;br /&gt;
Beim ATMEGA8 ist es möglich, dass RXCIE Bit im Register UCSRB zu setzen.&lt;br /&gt;
Dieser Interrupt wird immer ausglöst, wenn Daten erfolgreich empfangen wurden.&lt;br /&gt;
Zusätzlich braucht man die Routine:&lt;br /&gt;
&lt;br /&gt;
ISR (USART_RXC_vect) {&lt;br /&gt;
   //irgendein Code&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
natürlich muss &amp;quot;Global Interrupt Enable&amp;quot; Aktiviert sein.&lt;br /&gt;
!! Nur getestet beim ATMEGA8 !!&lt;br /&gt;
&lt;br /&gt;
[BAUSTELLE! Aus Lerngründen eventuell als eigenen UART-Interrupt-Block hinter den grundlegenden Interrupt-Teil im Tutorial und hier eine kurze Einführung und einen Verweis darauf anbieten.]&lt;br /&gt;
&lt;br /&gt;
* Unterschied Polling-Betrieb (bisher, oben) und Interrupt-Betrieb&lt;br /&gt;
* Empfangen (Receive)&lt;br /&gt;
** Verändertes UART-Init, ISR (RXC), ggf. Fallstricke ([http://www.mikrocontroller.net/topic/84256#707214 UDR in der ISR lesen!]), Philosophie einer ISR (kurz und schmerzlos), Datenaustausch ISR zu Restprogramm (volatile)&lt;br /&gt;
** Einfachstbeispiel ([http://www.mikrocontroller.net/topic/84228#707052 Echo] (noch buggy beim Datenzugriff, siehe Lit. 2+3!)), ggf. LED zur ISR-Empfangsanzeige oder Overflow-Anzeige&lt;br /&gt;
* FIFO-Puffer, Ringpuffer&lt;br /&gt;
* Senden (Transmit)&lt;br /&gt;
** Variante &amp;quot;UART Data Register Empty&amp;quot; (UDRE) &lt;br /&gt;
** Variante &amp;quot;UART Transmit Complete&amp;quot; (TXC) &lt;br /&gt;
* Fertige UART-Bibliotheken (-&amp;gt;Fleury, -&amp;gt;Procyon)&lt;br /&gt;
* Literatur: &lt;br /&gt;
** 1/ [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=48188 avrfreaks.net Tutorial] inkl. Diskussion (engl.)&lt;br /&gt;
** 2/ avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_16bitio Why do some 16-bit timer registers sometimes get trashed?]&lt;br /&gt;
** 3/ [[Interrupt]] und atomarer Datenzugriff&lt;br /&gt;
&lt;br /&gt;
==Software-UART==&lt;br /&gt;
&lt;br /&gt;
Falls die Zahl der vorhandenen Hardware-UARTs nicht ausreicht, können weitere Schnittstellen über sogennante Software-UARTs ergänzt werden. Es gibt dazu (mindestens) zwei Ansätze: &lt;br /&gt;
* Der bei AVRs üblichste Ansatz basiert auf dem Prinzip, dass ein externer Interrupt-Pin für den Empfang (&amp;quot;RX&amp;quot;) genutzt wird. Das Startbit löst den Interrupt aus, in der Interrupt-Routine (ISR) wird der externe Interrupt deaktiviert und ein Timer aktiviert. In der Interrupt-Routine des Timers wird der Zustand des Empfangs-Pins entsprechend der Baudrate abgetastet. Nach Empfang des Stop-Bits wird der externe Interrupt wieder aktiviert. Senden kann über einen beliebigen Pin (&amp;quot;TX&amp;quot;) erfolgen, der entsprechend der Baudrate und dem zu sendenden Zeichen auf 0 oder 1 gesetzt wird. Die Implementierung ist nicht ganz einfach, es existieren dazu aber fertige Bibliotheken (z.B. bei [http://www.avrfreaks.net/ avrfreaks] oder in der [http://hubbard.engr.scu.edu/embedded/avr/avrlib/ Procyon avrlib]).&lt;br /&gt;
* Ein weiterer Ansatz erfordert keinen Pin mit &amp;quot;Interrupt-Funktion&amp;quot; aber benötigt mehr Rechenzeit. Jeder Input-Pin kann als Empfangspin (RX) dienen. Über einen Timer wird der Zustand des RX-Pins mit einem vielfachen der Baudrate abgetastet (dreifach scheint üblich) und High- bzw. Lowbits anhand einer Mindestanzahl identifiziert. (Beispiel: &amp;quot;Generic Software Uart&amp;quot; Application-Note von IAR)&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (z.B. ATtiny26 oder ATmega48,88,168,169) verfügen über ein Universal Serial Interface (USI), das teilweise UART-Funktion übernehmen kann. Atmel stellt eine Application-Note bereit, in der die Nutzung des USI als UART erläutert wird (im Prinzip &amp;quot;Hardware-unterstützter Software-UART&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==Handshaking==&lt;br /&gt;
Wenn der Sender ständig sendet, wird irgendwann der Fall eintreten, daß der Empfänger nicht bereit ist, neue Zeichen zu empfangen. In diesem Fall muß durch ein &#039;&#039;&#039;Handshake-Verfahren&#039;&#039;&#039; die Situation bereinigt werden. Handshake bedeutet nichts anderes, als daß der Empfänger dem Sender mitteilt, daß er zur Zeit keine Daten annehmen kann und der Sender die Übertragung der nächsten Zeichen solange einstellen soll, bis der Empfänger signalisiert, daß er wieder Zeichen aufnehmen kann.&lt;br /&gt;
===Hardwarehandshake (RTS/CTS)===&lt;br /&gt;
Beim Hardwarehandshake werden zusätzlich zu den beiden Daten-Übertragungsleitungen noch 2 weitere Leitungen benötigt: &#039;&#039;&#039;RTS&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;equest &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end) und &#039;&#039;&#039;CTS&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end). Jeder der beiden Kommunikationspartner ist verpflichtet, bevor ein Zeichen gesendet wird, den Zustand der &#039;&#039;&#039;RTS&#039;&#039;&#039; Leitung zu überprüfen. Nur wenn die Gegenstelle darauf Empfangsbereitschaft signalisiert, darf das Zeichen gesendet werden. Um der Gegenstelle zu signalisieren, daß sie zur Zeit keine Zeichen schicken soll, wird die Leitung &#039;&#039;&#039;CTS&#039;&#039;&#039; benutzt.&lt;br /&gt;
&lt;br /&gt;
===Softwarehandshake (XON/XOFF)===&lt;br /&gt;
Beim Softwarehandshake sind keine speziellen Leitungen notwendig. Statt dessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, daß Senden einzustellen bzw. wieder aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;XOFF&#039;&#039;&#039; Aufforderung das Senden einzustellen&lt;br /&gt;
* &#039;&#039;&#039;XON&#039;&#039;&#039;  Gegenstelle darf wieder senden&lt;br /&gt;
&lt;br /&gt;
Nachteilig bei einem Softwarehandshake ist es, daß dadurch keine direkte binäre Datenübertragung mehr möglich ist. Von den möglichen 256 Bytewerten werden ja 2 (nämlich &#039;&#039;&#039;XON&#039;&#039;&#039; und &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus.&lt;br /&gt;
&lt;br /&gt;
==Fehlersuche==&lt;br /&gt;
Erstaunlich oft wird im Forum der Hilferuf laut: &amp;quot;Meine UART funktioniert nicht, was mache ich falsch&amp;quot;. In der überwiegenden Mehrzahl der Fälle stellt sich dann heraus, daß es sich um ein Hardwareproblem handelt, wobei da wiederrum der Löwenanteil auf das Konto einer nicht korrekt eingestellten Taktrate geht: Der µC benutzt nicht einen angeschlossenen Quarz, so wie er auch im Programm eingetragen ist, sondern läuft immer noch mit dem internen RC-Takt. Daraus resultiert aber auch, daß der Baudraten Konfigurationswert falsch berechnet wird.&lt;br /&gt;
&lt;br /&gt;
Eine Checkliste zum Aufspüren solcher Fehler findet sich [[AVR_Checkliste#UART.2FUSART|hier]].&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Analoge Eingangswerte werden in der Regel über den AVR Analog-Digital-Converter (AD-Wandler, ADC) eingelesen, der in vielen Typen verfügbar ist (typisch 10bit Auflösung). Durch diesen werden analogen Signale (Spannungen) in digitale Zahlenwerte gewandelt. Bei AVRs, die über keinen internen AD-Wandler verfügen (z.B. ATmega162, ATtiny2313), kann durch externe Beschaltung (R/C-Netzwerk und &amp;quot;Zeitmessung&amp;quot;) die Funktion des AD-Wandlers &amp;quot;emuliert&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
Es existieren keine AVRs mit eingebautem Digital-Analog-Konverter (DAC). Diese Funktion muss durch externe Komponenten nachgebildet werden (z.B. PWM und &amp;quot;Glättung&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Unabhängig davon besteht natürlich immer die Möglichkeit, spezielle Bausteine zur Analog-Digital- bzw. Digital-Analog-Wandlung zu nutzen und diese über eine digitale Schnittstelle (z.b. SPI oder I2C) mit einem AVR anzusteuern.&lt;br /&gt;
&lt;br /&gt;
== AC (Analog Comparator) ==&lt;br /&gt;
&lt;br /&gt;
Der Comparator vergleicht 2 Spannungen an den Pins AIN0 und AIN1 und gibt einen Status aus welche der beiden Spannungen größer ist. AIN0 Dient dabei als Referenzspannung (Sollwert) und AIN1 als Vergleichsspannung (Istwert). Als Referenzspannung kann auch alternativ eine interne Referenzspannung ausgewählt werden.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Liegt die Vergleichsspannung (IST) unter der der Referenzspannung (SOLL) gibt der Comperator eine logische 0 aus. ISt die Vergleichsspannung hingegen größer als die Referenzspannung wird eine logische 1 ausgegeben.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Der Comperator arbeitet völlig autark bzw. parallel zum Prozessor. Für mobile Anwendungen empfiehlt es sich ihn abzuschalten sofern er nicht benötigt wird, da er ansonsten Strom benötigt. Der Comparator kann Interruptgesteuert abgefragt werden oder im Pollingbetrieb.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Steuer- bzw. Statusregister ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ACSR (0x28) - Analog Comparator Status Register&amp;lt;BR&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACD&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;AINBG&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACO&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACI&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | n/a&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 7 ACD - Analog Comperator Disable: 0 = Comparator ein, 1 = Comparator aus. Wird dieses Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 ACIE ggf. abgeschaltet werden.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 6 AINBG - Analog Comparator Bandgap Select: Ermöglicht das umschalten zwischen interner und externer Referenzspannung. 1 = interne (~1,3 Volt), 0 = externe Referenzspannung (an Pin AIN0)&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 5 ACO - Analog Coparator Output: Hier wird das Ergebnis des Vergleichs angezeigt. Es liegt typischerweise nach 1-2 Taktzyklen vor. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ergebnis: &amp;lt;BR&amp;gt;IST &amp;lt; SOLL --&amp;gt; 0&amp;lt;BR&amp;gt;&lt;br /&gt;
IST &amp;gt; SOLL --&amp;gt; 1&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4 ACI - Analog Comparator Interrupt Flag: Dieses Bit wird von der Hardware gesetzt wenn ein Interruptereignis das in Bit 0 und 1 definiert ist eintritt. Dieses Bit löst noch keinen Interrupt aus! Die Interruptroutine wird nur dann ausgeführt wenn das Bit 3 ACIE gesetzt ist und global Interrupts erlaubt sind (I-Bit in SREG=1). Das Bit 4 ACI wird wieder gelöscht wenn die Interruptroutine ausgeführt wurde oder wenn manuell das Bit auf 1 gesetzt wird. Das Bit kann für Abfragen genutzt werden, steuert oder konfuguriert aber nicht den Comparator.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 3 ACIE - Analog Comparator Interrupt Enable: Ist das Bit auf 1 gesetzt wird immer ein Interrupt ausgelöst wenn das Ereignis das in Bit 1 und 0 definiert ist eintritt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 2 ACIC - Analog Comparator Input Capture Enable: Wird das Bit gesetzt wird der Comparatorausgang intern mit dem Counter 1 verbunden. Es könnten damit z.b. die Anzahl der Vergleiche im Counter1 gezählt werden. Um den Comparator an den Timer1 Input Capture Interrupt zu verbinden muß im Timerregister das TICIE1 Bit auf 1 gesetzt werden. Der Trigger wird immer dann ausgelöst wenn das in Bit 1 und 0 definierte Ereignis eintritt.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 1,0 ACIS1,ACIS0 - Analog Comparator Interrupt select: Hier wird definiert welche Ereignisse einen Interrupt auslösen sollen:&amp;lt;BR&amp;gt;&lt;br /&gt;
00 = Interrupt auslösen bei jedem Flankenwechsel&amp;lt;BR&amp;gt;&lt;br /&gt;
10 = Interrupt auslösen bei fallender Flanke&amp;lt;BR&amp;gt;&lt;br /&gt;
11 = Interrupt auslösen bei steigender Flanke&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Werden diese Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 gelöscht werden.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der Analog-Digital-Konverter (ADC) wandelt analoge Signale in digitale Werte um, welche vom Controller interpretiert werden können. Einige AVR-Typen haben bereits einen mehrkanaligen Analog-Digital-Konverter eingebaut. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst werden kann, wird durch die Auflösung des ADC in Anzahl Bits angegeben, man hört bzw. liest jeweils von 8-Bit-ADC oder 10-Bit-ADC oder noch höher. ADCs die in AVRs enthalten sind haben zur Zeit eine maximale Auflösung von 10-Bit.&lt;br /&gt;
&lt;br /&gt;
Ein ADC mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des Analog-Digital-Konverters ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Der interne ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem Analog-Digital-Wandler (ADC) zurückgreifen, die über mehrere Kanäle verfügen. Kanäle heißt in diesem Zusammenhang, dass zwar bis zu zehn analoge Eingänge am AVR verfügbar sind, aber nur ein &amp;quot;echter&amp;quot; Analog-Digital-Wandler zur Verfügung steht, vor der eigentlichen Messung ist also einzustellen, welcher Kanal (&amp;quot;Pin&amp;quot;) mit dem Wandler verbunden und gemessen wird.&lt;br /&gt;
&lt;br /&gt;
Die Umwandlung innerhalb des AVR basiert auf der schrittweisen Näherung. Beim AVR müssen die Pins &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AVCC&#039;&#039;&#039; beschaltet werden. Für genaue Messungen sollte AVCC über ein L-C Netzwerk mit VCC verbunden werden, um Spannungsspitzen und -einbrüche vom Analog-Digital-Wandler fernzuhalten. Im Datenblatt findet sich dazu eine Schaltung, die 10uH und 100nF vorsieht.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Analog-Digital-Wandlung wird auf eine Referenzspannung bezogen. Aktuelle AVRs bieten drei Möglichkeiten zur Wahl dieser Spannung:&lt;br /&gt;
&lt;br /&gt;
* Eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; am Anschlusspin &#039;&#039;&#039;AREF&#039;&#039;&#039;. Die minimale (externe) Referenzspannung darf jedoch nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. &lt;br /&gt;
&lt;br /&gt;
* Verfügt der AVR über eine interne Referenzspannung, kann diese genutzt werden. Alle aktuellen AVRs mit internem AD-Wandler sollten damit ausgestattet sein (vgl. Datenblatt: 2,56V oder 1,1V je nach Typ). Das Datenblatt gibt auch über die Genauigkeit dieser Spannung Auskunft.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;interne&#039;&#039;&#039; Referenzspannung wird auf &#039;&#039;&#039;Vcc&#039;&#039;&#039; bezogen, eine externe Referenzspannung auf &#039;&#039;&#039;GND (Masse)&#039;&#039;&#039;. Davon unabhängig werden digitalisierte Spannungen &#039;&#039;&#039;immer&#039;&#039;&#039; auf GND bezogen. Beim ATMEGA8 z.B. ist der Pin AREF über 32kOhm mit GND verbunden, d.h. man muss diese doch extrem niedrige Eingangsimpedanz mit in die Berechnung für einen Spannungsteiler einbeziehen, bzw. kann diesen Widerstand als R2 gleich mit benutzen. Formel für Spannungsteiler: Udiv = U / ((R1 + R2) / R2)&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung von Vcc oder der internen Referenz wird empfohlen, einen Kondensator zwischen dem AREF-Pin und GND anzuordnen. Die Festlegung, welche Spannungsreferenz genutzt wird, erfolgt z.B. beim ATmega16 mit den Bits REFS1/REFS0 im ADMUX-Register. Die zu messende Spannung muss im Bereich zwischen &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AREF&#039;&#039;&#039; (egal ob intern oder extern) liegen. &lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; kann in zwei verschiedenen Betriebsarten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
; Einfache Wandlung (Single Conversion) : In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestoßen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
; Frei laufend (Free Running) : In dieser Betriebsart erfasst der Wandler permanent die anliegende Spannung und schreibt diese in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Die Register des ADC ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; verfügt über eigene Register. Im Folgenden die Registerbeschreibung eines  ATMega16, welcher über 8 ADC-Kanäle verfügt. Die Register unterscheiden sich jedoch nicht erheblich von denen anderer AVRs (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister A.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den &#039;&#039;&#039;ADC&#039;&#039;&#039; verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADEN&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able)&lt;br /&gt;
:Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren. Wenn das Bit nicht gesetzt ist, können die Pins wie normale I/O-Pins verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADSC&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion)&lt;br /&gt;
:Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.&lt;br /&gt;
:Wenn das Bit nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.&lt;br /&gt;
:Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADFR&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;F&#039;&#039;&#039;ree &#039;&#039;&#039;R&#039;&#039;&#039;un select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Ist das Bit auf 1 gesetzt arbeitet der ADC im Freerunning Modus. Dabe iwird das Datenregister permanent aktualisiert. Ist das Bit hingegen auf 0 gesetzt macht der ADC nur eine Single Conversion. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIF&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag)&lt;br /&gt;
:Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt, sobald eine Umwandlung erfolgt ist und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert wurde. Das Bit wird bei lesendem Zugriff auf &#039;&#039;&#039;ADC(L,H)&#039;&#039;&#039; automatisch (d.h. durch die Hardware) gelöscht.&lt;br /&gt;
:Wenn das &#039;&#039;&#039;ADIE&#039;&#039;&#039; Bit sowie das &#039;&#039;&#039;I-Bit&#039;&#039;&#039; im AVR &#039;&#039;&#039;Statusregister&#039;&#039;&#039; gesetzt ist, wird der &#039;&#039;&#039;ADC Interrupt&#039;&#039;&#039; ausgelöst und die Interrupt-Behandlungsroutine aufgerufen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn die Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 in das Register geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIE&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;, dann wird der &#039;&#039;&#039; ADC-Interrupt&#039;&#039;&#039; aktiviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADPS2...ADPS0&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;P&#039;&#039;&#039;rescaler &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
:Der &#039;&#039;&#039;ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz ergibt.&lt;br /&gt;
:Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
TF_{min}=\frac{CLK}{200\,\mathrm{kHz}}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50\,\mathrm{kHz}}=\frac{4000000}{50000}=\mathbf{80}&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im Interesse der schnelleren Wandlungszeit werden wir hier den Faktor 32 einstellen.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCL&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ADCH&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC &#039;&#039;&#039; Data Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn eine Umwandlung abgeschlossen ist, befindet sich der gemessene Wert in&lt;br /&gt;
diesen beiden Registern. Von &#039;&#039;&#039;ADCH&#039;&#039;&#039; werden nur die beiden niederwertigsten Bits verwendet. Es müssen immer beide Register ausgelesen werden, und zwar immer &#039;&#039;&#039;in der Reihenfolge: ADCL, ADCH&#039;&#039;&#039;. &lt;br /&gt;
Der effektive Messwert ergibt sich dann zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCL;       // mit uint16_t x&lt;br /&gt;
x += (ADCH&amp;lt;&amp;lt;8); // in zwei Zeilen (LSB/MSB-Reihenfolge und&lt;br /&gt;
                // C-Operatorpriorität sichergestellt)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADMUX&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Mu&#039;&#039;&#039;ltiple&#039;&#039;&#039;x&#039;&#039;&#039;er Select Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Register wird der zu messende Kanal ausgewählt. Beim 90S8535&lt;br /&gt;
kann jeder Pin von Port A als &#039;&#039;&#039;ADC&#039;&#039;&#039;-Eingang verwendet werden (=8 Kanäle).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADLAR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX4&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX3&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;REFS1...REFS0&#039;&#039;&#039; (&#039;&#039;&#039;Ref&#039;&#039;&#039;erence&#039;&#039;&#039;S&#039;&#039;&#039;election Bits)&lt;br /&gt;
:Mit diesen Bits kann die Referenzspannung eingestellt werden:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Referenzspanung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Externes AREF&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | AVCC als Referenz&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Interne 2,56 Volt&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADLAR&#039;&#039;&#039; (&#039;&#039;&#039;ADC &#039;&#039;&#039; &#039;&#039;&#039;L&#039;&#039;&#039;eft &#039;&#039;&#039;A&#039;&#039;&#039;djust &#039;&#039;&#039;R&#039;&#039;&#039;esult)&lt;br /&gt;
:Das ADLAR Bit verändert das Aussehen des Ergebnisses der AD-Wandlung. Bei einer logischen 1 wird das Ergebnis linksbündig ausgegeben, bei einer 0 rechtsündig. Eine Änderung in diesem Bit beeinflusst das Ergebnis sofort, ganz egal ob bereits eine Wandlung läuft.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX4...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 5 Bits wird der zu messende Kanal bestimmt. Wenn man einen einfachen 1-kanaligen ADC verwendet wird einfach die entsprechende Pinnummer des Ports in die Bits 0...2 eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
:Eine Empfehlung ist deswegen diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren des ADC ====&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039; ADC&#039;&#039;&#039; zu aktivieren, müssen wir das &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bit im &#039;&#039;&#039;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
Ein kleines Beispiel für den &amp;quot;single conversion&amp;quot;-Mode bei einem ATmega169 und Nutzung der internen Referenzspannung (beim &#039;169 1,1V bei anderen AVRs auch 2,56V). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler einstellen. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1023 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint16_t ReadChannel(uint8_t mux)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
&lt;br /&gt;
  ADMUX = mux;                      // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler &lt;br /&gt;
                               // setzen auf 8 (1) und ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  /* nach Aktivieren des ADC wird ein &amp;quot;Dummy-Readout&amp;quot; empfohlen, man liest&lt;br /&gt;
     also einen Wert und verwirft diesen, um den ADC &amp;quot;warmlaufen zu lassen&amp;quot; */&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
     ;     // auf Abschluss der Konvertierung warten &lt;br /&gt;
  }&lt;br /&gt;
  result = ADCW;  // ADCW muss einmal gelesen werden,&lt;br /&gt;
                  // sonst wird Ergebnis der nächsten Wandlung&lt;br /&gt;
                  // nicht übernommen.&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  result = 0; &lt;br /&gt;
  for( i=0; i&amp;lt;4; i++ )&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);            // eine Wandlung &amp;quot;single conversion&amp;quot;&lt;br /&gt;
    while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
      ;   // auf Abschluss der Konvertierung warten&lt;br /&gt;
    }&lt;br /&gt;
    result += ADCW;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Beispielaufrufe: */&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
&lt;br /&gt;
  adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -&amp;gt; Channel 0 */&lt;br /&gt;
  ...&lt;br /&gt;
  adcval = ReadChannel(2); /* MUX-Bits auf 0b0010 -&amp;gt; Channel 2 */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekennzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Das Löschen des ADIF-Flags sollte, &#039;&#039;&#039;entgegen&#039;&#039;&#039; der [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_intbits FAQ], mit&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADIF);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
erfolgen. Die Methode in der FAQ eignet sich nur für Register in denen &#039;&#039;&#039;nur&#039;&#039;&#039; Interrupt-Flags stehen.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Analog-Digital-Wandlung ohne internen ADC ===&lt;br /&gt;
&lt;br /&gt;
==== Messen eines Widerstandes ====&lt;br /&gt;
&lt;br /&gt;
Analoge Werte lassen sich ohne Analog-Digital-Wandler auch indirekt ermitteln. Im Folgenden wird die Messung des an einem Potentiometer eingestellten Widerstands anhand der Ladekurve eines Kondensators erläutert. Bei dieser Methode wird nur ein Portpin benötigt, ein Analog-Digital-Wandler oder Analog-Comparator ist nicht erforderlich. Es wird dazu ein Kondensator und der Widerstand (das Potentiometer) in Reihe zwischen Vorsorgungsspannung und Masse/GND geschaltet (sogen. RC-Netzwerk). Zusätzlich wird eine Verbindung der Leitung zwischen Kondensator und Potentiometer zu einem Portpin des Controllers hergestellt. Die folgende Abbildung verdeutlicht die erforderliche Schaltung. &lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wird der Portpin des Controllers auf Ausgang konfiguriert (im Beispiel &#039;&#039;DDRD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) und dieser Ausgang auf Logisch 1 (&amp;quot;High&amp;quot;, &#039;&#039;PORTD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) geschaltet, liegt an beiden &amp;quot;Platten&amp;quot; des Kondensators das gleiche Potential &#039;&#039;&#039;VCC&#039;&#039;&#039; an und der Kondensator somit entladen. (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so, da an beiden Seiten des Kondensators das gleiche Potential anliegt und somit eine Potentialdifferenz von 0V besteht =&amp;gt; Kondensator ist entladen).&lt;br /&gt;
&lt;br /&gt;
Nach einer gewissen Zeit ist der Kondensator entladen und der Portpin wird als Eingang konfiguriert (&#039;&#039;DDRD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2); PORTD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2)&#039;&#039;), wodurch dieser hochohmig wird. Der Status des Eingangspin (in PIND) ist Logisch 1 (High). Der Kondensator lädt sich jetzt über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), wird das Eingangssignal als LOW erkannt (Bit in PIND wird 0). Die Zeitspanne zwischen der Umschaltung von Entladung auf Aufladung und dem Wechsel des Eingangssignals von High auf Low ist ein Maß für den am Potentiometer eingestellten Widerstand. Zur Zeitmessung kann einer der im Controller vorhandenen Timer genutzt werden. Der 220 Ohm Widerstand dient dem Schutz des Controllers. Es würde sonst bei Maximaleinstellung des Potentionmeters (hier 0 Ohm) ein zu hoher Strom fließen, der die Ausgangsstufe des Controllers zerstört. &lt;br /&gt;
&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B: 0...100 % oder so) umzurechnen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Link 404 =&amp;gt; auskommentiert, mthomas 9.2.2008 &lt;br /&gt;
Ein Beispielprogramm findet sich auf [http://www.mypage.bluewin.ch/ch_schifferle/ Christian Schifferles Web-Seite] im Archiv &#039;&#039;ATMEL.ZIP&#039;&#039;, welches unter den Titel &#039;&#039;Tutorial &amp;quot;Programmieren mit C für Atmel Mikrocontroller&#039;&#039; heruntergeladen werden kann. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ADC über Komparator ====&lt;br /&gt;
&lt;br /&gt;
Es gibt einen weiteren Weg, eine analoge Spannung mit Hilfe des&lt;br /&gt;
Komparators, welcher in fast jedem AVR integriert ist, zu messen. Siehe dazu&lt;br /&gt;
auch die Application Note AVR400 von Atmel.&lt;br /&gt;
&lt;br /&gt;
Dabei wird das zu messende Signal auf den invertierenden Eingang&lt;br /&gt;
des Komparators geführt. Zusätzlich wird ein Referenzsignal an den nicht&lt;br /&gt;
invertierenden Eingang des Komparators angeschlossen. Das Referenzsignal wird&lt;br /&gt;
hier auch wieder über ein RC-Glied erzeugt, allerdings mit festen Werten für R&lt;br /&gt;
und C.&lt;br /&gt;
&lt;br /&gt;
[[Image:ADC ueber Komparator.gif]]&lt;br /&gt;
&lt;br /&gt;
Das Prinzip der Messung ist nun dem vorhergehenden recht&lt;br /&gt;
ähnlich. Durch Anlegen eines LOW-Pegels an Pin 2 wird der Kondensator zuerst&lt;br /&gt;
einmal entladen. Auch hier muss darauf geachtet werden, dass der Entladevorgang&lt;br /&gt;
genügend lang dauert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Nun wird Pin 2 auf HIGH gelegt. Der Kondensator wird geladen. Wenn die Spannung&lt;br /&gt;
über dem Kondensator die am Eingangspin anliegende Spannung erreicht hat&lt;br /&gt;
schaltet der Komparator durch. Die Zeit, welche benötigt wird, um den&lt;br /&gt;
Kondensator zu laden kann nun auch wieder als Maß für die Spannung an Pin 1&lt;br /&gt;
herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Ich habe es mir gespart, diese Schaltung auch aufzubauen und&lt;br /&gt;
zwar aus mehreren Gründen:&lt;br /&gt;
&lt;br /&gt;
# 3 Pins notwendig.&lt;br /&gt;
# Genauigkeit vergleichbar mit einfacherer Lösung.&lt;br /&gt;
# War einfach zu faul.&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung liegt allerdings darin, dass damit&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines Digital-Analog-Konverters (&#039;&#039;&#039;DAC&#039;&#039;&#039;) können wir nun auch Analogsignale ausgeben. Es gibt hier mehrere Verfahren. &amp;lt;!-- Wenn wir beim ADC die Möglichkeit haben, mit externen Komponenten zu operieren, müssen wir bei der DAC-Wandlung mit dem auskommen, was der Controller selber zu bieten hat. --mt: hmm, richtig? verstaendlich? redundant? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DAC über mehrere digitale Ausgänge ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir an den Ausgängen des Controllers ein entsprechendes&lt;br /&gt;
Widerstandsnetzwerk aufbauen haben wir die Möglichkeit, durch die Ansteuerung&lt;br /&gt;
der Ausgänge über den Widerständen einen Addierer aufzubauen, mit dessen&lt;br /&gt;
Hilfe wir eine dem Zahlenwert proportionale Spannung erzeugen können. Das&lt;br /&gt;
Schaltbild dazu kann etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
[[Image:DAC R2R.gif]]&lt;br /&gt;
&lt;br /&gt;
Es sollten selbstverständlich möglichst genaue Widerstände verwendet&lt;br /&gt;
werden, also nicht unbedingt solche mit einer Toleranz von 10% oder mehr.&lt;br /&gt;
Weiterhin empfiehlt es sich, je nach Anwendung den Ausgangsstrom über einen&lt;br /&gt;
Operationsverstärker zu verstärken.&lt;br /&gt;
&lt;br /&gt;
=== PWM (Pulsweitenmodulation) ===&lt;br /&gt;
&lt;br /&gt;
Wir kommen nun zu einem Thema, welches in aller Munde ist, aber viele&lt;br /&gt;
Anwender verstehen nicht ganz, wie &#039;&#039;&#039; PWM&#039;&#039;&#039; eigentlich funktioniert.&lt;br /&gt;
&lt;br /&gt;
Wie wir alle wissen, ist ein Mikrocontroller ein rein digitales Bauteil.&lt;br /&gt;
Definieren wir einen Pin als Ausgang, dann können wir diesen Ausgang entweder&lt;br /&gt;
auf HIGH setzen, worauf am Ausgang die Versorgungsspannung &#039;&#039;&#039; Vcc&#039;&#039;&#039; anliegt, oder aber wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt. Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen HIGH und LOW umschalten? - Richtig, wir erhalten eine Rechteckspannung, wie die folgende Abbildung zeigt:&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 1.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Rechteckspannung hat nun einen arithmetischen Mittelwert, &lt;br /&gt;
der je nach Pulsbreite kleiner oder größer ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 2.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun diese pulsierende Ausgangsspannung noch über ein RC-Glied filtern/&amp;quot;glätten&amp;quot;, dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVRs können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. &lt;br /&gt;
Dazu dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt, weshalb ein Blick ins entsprechende Datenblatt dringend angeraten wird.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren, müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| PWM-Modus des Timers ist nicht aktiv.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Timer/Counter zählt nun permanent von 0 bis zur Obergrenze&lt;br /&gt;
und wieder zurück, er wird also als sogenannter Auf-/Ab Zähler betrieben. &lt;br /&gt;
Die Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss mit den Bits &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; desselben&lt;br /&gt;
Registers die gewünschte Ausgabeart des Signals definiert werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Nicht invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Hochzählen und gesetzt beim&lt;br /&gt;
Herunterzählen.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Herunterzählen und gesetzt beim&lt;br /&gt;
Hochzählen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl, um beispielsweise den Timer/Counter als&lt;br /&gt;
nicht invertierenden 10-Bit PWM zu verwenden, heißt dann:&lt;br /&gt;
&lt;br /&gt;
alte Schreibweise (PWMxx wird nicht mehr akzeptiert)&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;PWM11)|(1&amp;lt;&amp;lt;PWM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
neue Schreibweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;WGM11)|(1&amp;lt;&amp;lt;WGM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Timer/Counter überhaupt läuft, müssen wir im Control&lt;br /&gt;
Register B &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; noch den gewünschten Takt (Vorteiler) einstellen und&lt;br /&gt;
somit auch die Frequenz des &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signals bestimmen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stop. Der Timer/Counter wird gestoppt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin 1, negative Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin 1, positive Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Also um einen Takt von CK / 1024 zu generieren, verwenden wir&lt;br /&gt;
folgenden Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1B = (1&amp;lt;&amp;lt;CS12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt muss nur noch der Vergleichswert festgelegt werden. Diesen&lt;br /&gt;
schreiben wir in das 16-Bit Timer/Counter Output Compare Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem Vergleichswert und dem generierten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal aufzeigen.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
Ach ja, fast hätte ich&#039;s vergessen. Das generierte &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal&lt;br /&gt;
wird am Output Compare Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; des Timers ausgegeben und leider können wir&lt;br /&gt;
deshalb auch beim AT90S2313 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren. Andere AVR-Typen verfügen über bis zu vier PWM-Ausgänge. Zu beachten ist außerdem, das wenn der OC Pin aktiviert ist, er nichtmehr wie üblich funktioniert und z.B. nicht einfach über PINx ausgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Ein Programm, dass an einem ATMega8 den Fast-PWM Modus verwendet, den Modus 14, könnte so aussehen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // OC1A auf Ausgang&lt;br /&gt;
  DDRB = (1 &amp;lt;&amp;lt; PB1 );&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // Timer 1 einstellen&lt;br /&gt;
  //  &lt;br /&gt;
  // Modus 14:&lt;br /&gt;
  //    Fast PWM, Top von ICR1&lt;br /&gt;
  //&lt;br /&gt;
  //     WGM13    WGM12   WGM11    WGM10&lt;br /&gt;
  //      1        1       1        0&lt;br /&gt;
  //&lt;br /&gt;
  //    Timer Vorteiler: 1&lt;br /&gt;
  //     CS12     CS11    CS10&lt;br /&gt;
  //       0        0       1&lt;br /&gt;
  //&lt;br /&gt;
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match&lt;br /&gt;
  //     COM1A1   COM1A0&lt;br /&gt;
  //       1        0&lt;br /&gt;
 &lt;br /&gt;
  TCCR1A = (1&amp;lt;&amp;lt;COM1A1) | (1&amp;lt;&amp;lt;WGM11);&lt;br /&gt;
  TCCR1B = (1&amp;lt;&amp;lt;WGM13) | (1&amp;lt;&amp;lt;WGM12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  //  den Endwert (TOP) für den Zähler setzen&lt;br /&gt;
  //  der Zähler zählt bis zu diesem Wert&lt;br /&gt;
&lt;br /&gt;
  ICR1 = 0x6FFF;&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  // der Compare Wert&lt;br /&gt;
  // Wenn der Zähler diesen Wert erreicht, wird mit&lt;br /&gt;
  // obiger Konfiguration der OC1A Ausgang abgeschaltet&lt;br /&gt;
  // Sobald der Zähler wieder bei 0 startet, wird der&lt;br /&gt;
  // Ausgang wieder auf 1 gesetzt&lt;br /&gt;
  //&lt;br /&gt;
  // Durch Verändern dieses Wertes, werden die unterschiedlichen&lt;br /&gt;
  // PWM Werte eingestellt.&lt;br /&gt;
&lt;br /&gt;
  OCR1A = 0x3FFF;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  while( 1 )&lt;br /&gt;
    ;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für Details der PWM Möglichkeiten, muß immer das jeweilge Datenblatt des Prozessors konsultiert werden, da sich die unterschiedlichen Prozessoren in ihren Möglichkeiten doch stark unterscheiden. Auch muß man aufpassen, welches zu setzende Bit in welchem Register sind. Auch hier kann es sein, dass gleichnamige Konfigurationsbits in unterschiedlichen Konfigurationsregistern (je nach konkretem Prozessortyp) sitzen.&lt;br /&gt;
&lt;br /&gt;
= LCD-Ansteuerung =&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung an der LCD-Controller-Platine ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; (ist in manchen Unterlagen auch &#039;&#039;&#039;EN&#039;&#039;&#039;  für &#039;&#039;Enable&#039;&#039; abgekürzt) benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert, ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitetet hat (diese Methode u.a. in der LCD-Library von Peter Fleury verwendet). Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt da wir wissen, welche Anschlüsse das LCDs benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin #-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin-µC&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND oder Poti (siehe oben)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD4 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD5 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD0 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD1 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD2 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD3 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man die Steuerleitungen EN und RS auf Pins an einem anderen Port legen möchte, kann man so wie in diesem [http://www.mikrocontroller.net/topic/88543#751982 Forumsbeitrag] vorgehen.&lt;br /&gt;
&lt;br /&gt;
Ok, alles ist verbunden, wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display? &lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
void lcd_data(unsigned char temp1);&lt;br /&gt;
void lcd_string(char *data);&lt;br /&gt;
void lcd_command(unsigned char temp1);&lt;br /&gt;
void lcd_enable(void);&lt;br /&gt;
void lcd_init(void);&lt;br /&gt;
void lcd_home(void);&lt;br /&gt;
void lcd_clear(void);&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y);&lt;br /&gt;
&lt;br /&gt;
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
&lt;br /&gt;
// LCD Befehle&lt;br /&gt;
&lt;br /&gt;
#define CLEAR_DISPLAY 0x01&lt;br /&gt;
#define CURSOR_HOME   0x02&lt;br /&gt;
&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
&lt;br /&gt;
#define LCD_PORT      PORTD&lt;br /&gt;
#define LCD_DDR       DDRD&lt;br /&gt;
#define LCD_RS        PD4&lt;br /&gt;
#define LCD_EN        PD5&lt;br /&gt;
// DB4 bis DB7 des LCD sind mit PD0 bis PD3 des AVR verbunden&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// sendet ein Datenbyte an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_data(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_RS);        // RS auf 1 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_command(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);        // RS auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;              // oberes Nibble holen&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;            // maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;            // unteres Nibble holen und maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// erzeugt den Enable-Puls&lt;br /&gt;
void lcd_enable(void)&lt;br /&gt;
{&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
    _delay_us(1);                   // kurze Pause&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/80900&lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Initialisierung: &lt;br /&gt;
// Muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
void lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
   LCD_DDR = LCD_DDR | 0x0F | (1&amp;lt;&amp;lt;LCD_RS) | (1&amp;lt;&amp;lt;LCD_EN);   // Port auf Ausgang schalten&lt;br /&gt;
&lt;br /&gt;
   // muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(15);&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x03;            &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);      // RS auf 0&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4 Bit Modus aktivieren &lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x02;&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4Bit / 2 Zeilen / 5x7&lt;br /&gt;
   lcd_command(0x28);&lt;br /&gt;
    &lt;br /&gt;
   // Display ein / Cursor aus / kein Blinken&lt;br /&gt;
   lcd_command(0x0C); &lt;br /&gt;
 &lt;br /&gt;
   // inkrement / kein Scrollen&lt;br /&gt;
   lcd_command(0x06);&lt;br /&gt;
&lt;br /&gt;
   lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
&lt;br /&gt;
void lcd_clear(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CLEAR_DISPLAY);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
&lt;br /&gt;
void lcd_home(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CURSOR_HOME);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)&lt;br /&gt;
&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t tmp;&lt;br /&gt;
&lt;br /&gt;
  switch (y) {&lt;br /&gt;
    case 1: tmp=0x80+0x00+x; break;    // 1. Zeile&lt;br /&gt;
    case 2: tmp=0x80+0x40+x; break;    // 2. Zeile&lt;br /&gt;
    case 3: tmp=0x80+0x10+x; break;    // 3. Zeile&lt;br /&gt;
    case 4: tmp=0x80+0x50+x; break;    // 4. Zeile&lt;br /&gt;
  }&lt;br /&gt;
  lcd_command(tmp);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_string(char *data)&lt;br /&gt;
{&lt;br /&gt;
    while(*data) {&lt;br /&gt;
        lcd_data(*data);&lt;br /&gt;
        data++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches die Funktionen benutzt, sieht zb. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen&lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    lcd_data(&#039;T&#039;);&lt;br /&gt;
    lcd_data(&#039;e&#039;);&lt;br /&gt;
    lcd_data(&#039;s&#039;);&lt;br /&gt;
    lcd_data(&#039;t&#039;);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
&lt;br /&gt;
    lcd_string(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist dabei, dass die Optimierung bei der Compilierung eingeschaltet ist, sonst stimmen die Zeiten der Funktionen _delay_us() und _delay_ms() nicht und der Code wird wesentlich länger (Siehe Dokumentation der libc im WinAVR).&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches eine Variable ausgibt, sieht zb. so aus.&lt;br /&gt;
Mittels der itoa() Funktion (itoa = &amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;nteger &amp;lt;b&amp;gt;To&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;A&amp;lt;/b&amp;gt;scii ) wird von einem Zahlenwert eine textuelle Repräsentierung ermittelt (sprich: ein String erzeugt) und dieser String mit der bereits vorhandenen Funktion lcd_string ausgegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen &lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Beispiel&lt;br /&gt;
int variable = 42;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe des Zeichens dessen ASCII-Code gleich dem Variablenwert ist&lt;br /&gt;
    // (Im Beispiel entspricht der ASCII-Code 42 dem Zeichen *)&lt;br /&gt;
    // http://www.code-knacker.de/ascii.htm&lt;br /&gt;
    lcd_data(variable);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
 &lt;br /&gt;
    // Ausgabe der Variable als Text in dezimaler Schreibweise&lt;br /&gt;
    {&lt;br /&gt;
       // ... umwandeln siehe FAQ Artikel bei www.mikrocontroller.net&lt;br /&gt;
       // WinAVR hat eine itoa()-Funktion, das erfordert obiges #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
       char Buffer[20]; // in diesem {} lokal&lt;br /&gt;
       itoa( variable, Buffer, 10 ); &lt;br /&gt;
&lt;br /&gt;
       // ... ausgeben  &lt;br /&gt;
       lcd_string( Buffer );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einrichten eines Projekts muss man zu der Datei mit dem Hauptprogramm auch die Datei lcd-routines.c in das Projekt aufnehmen. Dies geschieht beim AVR Studio unter Source Files im Fenster AVR GCC oder bei WinAVR im Makefile (z.B. durch SRC += lcd-routines.c).&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVRs sind für viele Steuerungsaufgaben zu schnell. Wenn wir beispielsweise eine LED oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die CPU-Frequenz verwenden, da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, Vorgänge in Zeitabständen durchzuführen, die geringer als die Taktfrequenz des Controllers sind. Selbstverständlich sollte die resultierende Frequenz auch noch möglichst genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist ganz einfach ein bestimmtes Register im µC, das völlig ohne Zutun des Programms, also per Hardware, hochgezählt wird. Das alleine wäre noch nicht allzu nützlich, wenn nicht dieses Hardwareregister bei bestimmten Zählerständen einen Interrupt auslösen könnte. Ein solches Ereignis ist der Overflow: Da die Bitbreite des Registers beschränkt ist, kommt es natürlich auch vor, dass der Zähler so hoch zählt, dass der nächste Zählerstand mit dieser Bitbreite nicht mehr darstellbar ist und der Zähler wieder auf 0 zurückgesetzt wird. Dieses Ereignis nennt man den Overflow und es ist möglich an dieses Ereignis einen Interrupt zu koppeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Alternativvorschlag mthomas &lt;br /&gt;
Jeder Timer verfügt über ein Zählerregister im Mikrocontroller, das automatisch und ohne Zutun des Programms von der Hardware weitergezählt wird. In einem einfachen Anwendungsfall stellt man den Timer auf eine Zählgeschwindigkeit (Frequenz) und kann dann anhand des Zählerstands ermitteln, wie viel Zeit vergangen ist. Das eigentlich Nützliche an Timern ist jedoch, dass man  bestimmte Zählerstände mit Interrupts verknüpfen kann, so dass der Controller beim Auftreten automatisch eine vom Anwender geschriebene Routine aufruft. Eines dieser möglichen Ereignis ist der Overflow ((Zähler-)Überlauf), der dann auftritt, wenn der Wert des Zählerregisters (Timer/Counter-Register) den maximal möglichen Wert überschreitet. Der Maximalwert wird durch die Bitbreite des Zählerregisters bestimmt (z.B. 255 bei 8-Bit Timern). Beim Überlauf/Overflow wird der Zähler durch die Hardware auf 0 zurückgesetzt und die Zählung beginnt von neuem. Wurde vorher der Overflow-Interrupt für den Timer aktiviert (im Timer Control Register) unterbricht der Controller automatisch die Ausführung des Hauptprogramms und verzweigt in die Interrupt-Routine des Anwenders.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auf den AT90S2313. Für andere Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer Auflösung von 65536. Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz, der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
== Der Vorteiler (Prescaler) ==&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler dient dazu, den CPU-Takt vorerst um einen einstellbaren Faktor zu reduzieren. Die so geteilte Frequenz wird den Eingängen der Timer zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024 einstellen, wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca. 4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw. Zählregister (TCNTx) mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle verfügen über mindestens einen, teilweise sogar zwei, 8-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird z.B bei AT90S2313 über folgende Register angesprochen (bei anderen Typen weitestgehend analog):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    TCCR0 |= (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst und die daran gebundene Interrupt-Routine abgearbeitet. Das TOV Flag &lt;br /&gt;
lässt sich durch das Hineinschreiben einer 1 und nicht wie erwartet einer 0 wieder zurücksetzen.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen außer den 8-Bit Timern auch 16-Bit Timer. Die 16-Bit Timer/Counter sind etwas komplexer aufgebaut als die 8-Bit Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM13&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM12 (CTC1)&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorteiler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt, und C der voreingestellte Vergleichswert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin T1, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin T1, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T1 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T1 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0, bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;TCNT1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;OCR1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;OCR1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;OCR1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;OCR1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäß folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]] [[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Ausserdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr. Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen kleiner 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4us warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.6 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t x;&lt;br /&gt;
&lt;br /&gt;
    x = 10;&lt;br /&gt;
&lt;br /&gt;
    while (x &amp;gt;= 0)&lt;br /&gt;
    {&lt;br /&gt;
      // tu was&lt;br /&gt;
&lt;br /&gt;
      x--;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog? ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Einige Controller haben einen eigenen Watchdog Oszillator, z.B. der Tiny2313 mit 128kHz. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde, beginnt der Counter von 0 an hochzuzählen. &lt;br /&gt;
Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern, müssen wir den Watchdog regelmäßig wieder neu starten bzw. rücksetzen (Watchdog Reset). &lt;br /&gt;
Dies sollte innerhalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern, muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten. Es müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. &lt;br /&gt;
Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist, wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei &#039;&#039;wdt.h&#039;&#039; (&#039;&#039;#include &amp;lt;avr/wdt.h&amp;gt;&#039;&#039;) in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch gestartet wird. &lt;br /&gt;
Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. &lt;br /&gt;
Falls ein anderer Wert gewünscht ist, so kann dies im Makfile in den Linker-Optionen eingetragen werden. &lt;br /&gt;
Dazu muss in der Zeile LDFLAGS folgende Option angefügt werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert&lt;br /&gt;
:Mögliche Timeoutwerte:&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Konstante&lt;br /&gt;
! Wert&lt;br /&gt;
! TimeOut&lt;br /&gt;
|- &lt;br /&gt;
| WDTO_15MS   &lt;br /&gt;
| 0&lt;br /&gt;
| 15 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_30MS   &lt;br /&gt;
| 1&lt;br /&gt;
| 30 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_60MS   &lt;br /&gt;
| 2&lt;br /&gt;
| 60 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_120MS   &lt;br /&gt;
| 3&lt;br /&gt;
| 120 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_250MS   &lt;br /&gt;
| 4&lt;br /&gt;
| 250 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_500MS   &lt;br /&gt;
| 5&lt;br /&gt;
| 500 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_1S   &lt;br /&gt;
| 6&lt;br /&gt;
| 1 S&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_2S   &lt;br /&gt;
| 7&lt;br /&gt;
| 2s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
== Watchdog-Anwendungshinweise ==&lt;br /&gt;
&lt;br /&gt;
Ob nun der Watchdog als Schutzfunktion überhaupt verwendet werden soll, hängt stark von der Anwendung, der genutzten Peripherie und dem Umfang und der Qualitätssicherung des Codes ab. Will man sicher gehen, dass ein Programm sich nicht in einer Endlosschleife verfängt, ist der Wachdog das geeignete Mittel dies zu verhindern. Weiterhin kann bei geschickter Programmierung der Watchdog dazu genutzt werden, bestimmte Stromsparfunktionen zu implementieren. Bei einigen neueren AVRs (z.B. dem ATTiny13) kann der Watchdog auch direkt als Timer genutzt werden, der den Controller aus einem Schlafmodus aufweckt. Auch dies kann im &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register eingestellt werden. Außerdem bietet der WD die einzige Möglichkeit einen beabsichtigten System-Reset (ein &amp;quot;richtiger Reset&amp;quot;, kein &amp;quot;jmp 0x0000&amp;quot;) ohne externe Beschaltung auszulösen, was z.B. bei der Implementierung eines Bootloaders nützlich ist. Bei bestimmten Anwendungen kann die Nutzung des WD als &amp;quot;ultimative Deadlock-Sicherung für nicht bedachte Zustände&amp;quot; natürlich immer als zusätzliche Sicherung dienen. &lt;br /&gt;
&lt;br /&gt;
Es besteht die Möglichkeit herauszufinden, ob ein Reset durch den Watchdog ausgelöst wurde (beim ATmega16 z.B. Bit WDRF in MCUCSR). Diese Information sollte auch genutzt werden, falls ein WD-Reset in der Anwendung nicht planmäßig implementiert wurde. Zum Beispiel kann man eine LED an einen freien Pin hängen, die nur bei einem Reset durch den WD aufleuchtet oder aber das &amp;quot;Ereignis WD-Reset&amp;quot; im internen EEPROM des AVR absichern, um die Information später z.B. über UART oder ein Display auszugeben (oder einfach den EEPROM-Inhalt über die ISP/JTAG-Schnittstelle auslesen).&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/77273#642501 Bug in ATtiny2313?]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt der Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft. Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe Abschnitt (TODO: verlinken) im Anhang.&lt;br /&gt;
&amp;lt;!--Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. vgl. [[AVR-GCC-Tutorial#Anhang|Anhang]])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.B. &#039;&#039;void TIMER0_OVF_vect(void)...&#039;&#039;). Der Unterschied im Vergleich zu ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben.  &amp;lt;!--Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)), als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur &lt;br /&gt;
in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechnungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechnungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0, PORTA ist danach 0b00000001. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-oder-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8 (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten &#039;&#039;Sleep-Modes&#039;&#039; (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_enable()&#039;&#039;&#039;&lt;br /&gt;
:aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_cpu()&#039;&#039;&#039;&lt;br /&gt;
: Versetzt den Controller in den Schlafmodus (sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt)&lt;br /&gt;
* &#039;&#039;&#039;sleep_disable()&#039;&#039;&#039;&lt;br /&gt;
:deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts.&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;allozieren&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich, um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory, bei AVR Controllern mit mehr als 64kB Flash auch elpm). Die Standard-Laufzeitbibliothek des avr-gcc (die avr-libc) stellt diese Funktionen nach Einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Lokale Variablen (eigentlich Konstanten) innerhalb von Funktionen können ebenfalls im Programmspeicher abgelegt werden. Dazu ist bei der Definition jedoch ein &#039;&#039;static&#039;&#039; voranzustellen, da solche &amp;quot;Variablen&amp;quot; nicht auf dem Stack bzw. (bei Optimierung) in Registern verwaltet werden können. Der Compiler &amp;quot;wirft&amp;quot; eine Warnung falls static fehlt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray2 };&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  static /*const*/ uint8_t pgmTestByteLocal PROGMEM = 0x55;&lt;br /&gt;
  static /*const*/ char pgmTestStringLocal[] PROGMEM = &amp;quot;im Flash&amp;quot;;&lt;br /&gt;
  // so nicht (static fehlt): char pgmTestStringLocalFalsch [] PROGMEM = &amp;quot;so nicht&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 // ...&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
    //...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte....&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWort);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;groß&amp;quot; (Stand avr-gcc 3.4.x). Damit ist der mögliche Speicherbereich für &amp;quot;Flash-Konstanten&amp;quot; auf 64kB begrenzt.  &amp;lt;!-- Einige avr-libc/pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher) (intern via Assembler Anweisung ELPM). Die Initialisierungswerde des Speicherinhalts jenseits der 64kB-Marke müssen dann jedoch auf anderem Weg angelegt werden (nicht PROGMEM, evtl. eigene Section und Linker-Optionen - TODO) /// alt - und nicht ganz korrekt: (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.)--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray enthält nun die Startadresse des Byte-Arrays pgmFooByteArray1&lt;br /&gt;
    // Allerdings würde ein direkter Zugriff mit diesem Pointer (z.B. temp=*ptrToArray)&lt;br /&gt;
    // &#039;&#039;&#039;nicht&#039;&#039;&#039; den Inhalt von pgmFooByteArray1[0] liefern, sondern von einer Speicherstelle&lt;br /&gt;
    // im &#039;&#039;&#039;RAM&#039;&#039;&#039;, die die gleiche Adresse hat wie pgmFooByteArray1[0]&lt;br /&gt;
    // Daher muss nun die Funktion pgm_read_byte() benutzt werden, die die in ptrToArray&lt;br /&gt;
    // enthaltene Adresse benutzt und auf das Flash zugreift.&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray enthält nun die Adresse des ersten Elements des Byte-Arrays pgmFooByteArray2&lt;br /&gt;
    // da im zweiten Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmFooByteArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Array aus Zeichenketten im Flash-Speicher ===&lt;br /&gt;
&lt;br /&gt;
Felder aus Zeichenketten im Flash-Speicher werden in zwei Schritten angelegt: Zuerst die einzelnen Elemente des Arrays und im Anschluss ein Array, in dem die Addressen der Zeichenketten abgelegt werden. Zum Auslesen wird zuerst die Adresse des i-ten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, auf das Element (die Zeichenkette) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const char str1[] PROGMEM = &amp;quot;first_A&amp;quot;;&lt;br /&gt;
const char str2[] PROGMEM = &amp;quot;second_A&amp;quot;;&lt;br /&gt;
const char str3[] PROGMEM = &amp;quot;third_A&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char *strarray1[] PROGMEM = {&lt;br /&gt;
	str1,&lt;br /&gt;
	str2,&lt;br /&gt;
	str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	int i, j, l;&lt;br /&gt;
	const char *pstrflash;&lt;br /&gt;
	char work[20], work2[20];&lt;br /&gt;
	// fuer Simulation: per volatile Optimierung verhindern, &lt;br /&gt;
	//                  da c nicht genutzt&lt;br /&gt;
	volatile char c;&lt;br /&gt;
	&lt;br /&gt;
	for ( i = 0; i &amp;lt; (sizeof(strarray1)/sizeof(strarray1[0]) ); i++ ) {&lt;br /&gt;
&lt;br /&gt;
		// setze Pointer auf die Addresse des i-ten Elements des&lt;br /&gt;
		// &amp;quot;Flash-Arrays&amp;quot; (str1, str2, ...)&lt;br /&gt;
		pstrflash = (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) );&lt;br /&gt;
		&lt;br /&gt;
		// kopiere den Inhalt der Zeichenkette von der&lt;br /&gt;
		// in pstrflash abgelegten Adresse in das work-Array&lt;br /&gt;
		// analog zu strcpy( work, strarray1[i]) wenn alles im RAM&lt;br /&gt;
		strcpy_P( work, pstrflash );&lt;br /&gt;
		// verkuerzt:&lt;br /&gt;
		strcpy_P( work2, (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) ) );&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
		// Zeichen-fuer-Zeichen&lt;br /&gt;
		l = strlen_P( pstrflash );&lt;br /&gt;
		for ( j=0; j &amp;lt; l; j++ ) {&lt;br /&gt;
			// analog zu c=strarray[i][j] wenn alles im RAM&lt;br /&gt;
			c = (char)( pgm_read_byte( pstrflash++ ) );&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	while (1) { ; }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: &amp;quot;How do I put an array of strings completely in ROM?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5)) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Vorsicht: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char textImFlashOK[] PROGMEM = &amp;quot;mit[]&amp;quot;; &lt;br /&gt;
// = Daten im &amp;quot;Flash&amp;quot;, textImFlashOK* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char* textImFlashProblem PROGMEM = &amp;quot;mit*&amp;quot;;&lt;br /&gt;
// Konflikt: Daten im BSS (lies: RAM), textImFlashFAIL* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der Initialisierungsstring von &amp;quot;textImFlashProblem&amp;quot; zu den Konstanten ans Ende des Programmcodes gelegt wird (BSS), von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte (und wird). Da der lesende Code (mittels pgm_read*) trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein weiters Problem des AVR-GCC (gesehen bei avr-gcc 3.4.1 und 3.4.2) bei der Anpassung an die Harvard-Architektur zu sein (konstanter Pointer auf variable Daten?!). Abhilfe (&amp;quot;Workaround&amp;quot;): Initialisierung bei Zeichenketten mit [] oder gleich im Code PSTR(&amp;quot;...&amp;quot;) nutzen.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden, ob es sich um eine Adresse im Flash oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind.&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die einen im Flash abgelegten String z.B. an eine UART ausgibt, würde dann so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void uart_puts_p(const char *text)&lt;br /&gt;
{&lt;br /&gt;
    char Zeichen;&lt;br /&gt;
&lt;br /&gt;
    while (Zeichen = pgm_read_byte(text))&lt;br /&gt;
    {   /* so lange, wie mittels pgm_read_byte ein Zeichen vom Flash gelesen&lt;br /&gt;
           werden konnte, welches nicht das &amp;quot;String-Endezeichen&amp;quot; darstellt */&lt;br /&gt;
&lt;br /&gt;
        /* Das gelesene Zeichen über die normalen Kanäle verschicken */&lt;br /&gt;
        uart_putc(Zeichen);&lt;br /&gt;
        text++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Flash in der Anwendung schreiben ===&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option (auch bekannt als Bootloader-Support) können Teile des Flash-Speichers auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. Bei wenigen &amp;quot;kleinen&amp;quot; AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm(Flash)- und Datenspeicher(RAM) auf. Der C-Standard und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiß&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, dass vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgegeben sind, die innerhalb weniger Taktzyklen aufeinanderfolgen müssen (&amp;quot;timed sequence&amp;quot;). Auch dies muss bei Nutzung der Funktionen aus der avr-libc/eeprom.h-Datei nicht selbst implementiert werden. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Siehe dazu folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt; // wird in aktuellen Versionen der avr-lib mit xx.h eingebunden&lt;br /&gt;
&lt;br /&gt;
// EEMEM wird bei aktuellen Versionen der avr-lib in eeprom.h definiert&lt;br /&gt;
// hier: definiere falls noch nicht bekannt (&amp;quot;alte&amp;quot; avr-libc)&lt;br /&gt;
#ifndef EEMEM&lt;br /&gt;
// alle Textstellen EEMEM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEMEM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
//...&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // &#039;Variablen&#039; eeFooByte geschrieben&lt;br /&gt;
//...&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z.B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    #define EEPROM_DEF 0xFF&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ( ( myByte = eeprom_read_byte(&amp;amp;eeFooByte) ) == EEPROM_DEF ) {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
//...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprüft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und möglicherweise noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,3);&lt;br /&gt;
&lt;br /&gt;
    /* dito etwas anschaulicher aber &amp;quot;unnütze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fließkommazahlen lassen sich recht praktisch über eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t i[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    uint8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEMEM;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
//...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklaration abgeleiteten EEPROM-Inhalt in eine Datei geschrieben werden (übliche Dateiendung: .eep, Daten im Intel Hex-Format). Damit können recht elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen (siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles). Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden (Write EEPROM), wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse (vgl. Datenblatt Abschnitt Fuse Bits) die vorherigen Daten (EESAVE programmed = 0), deren Position möglicherweise nicht mehr mit der Belegung im aktuellen Programm übereinstimmt oder den Standardwert nach &amp;quot;Chip Erase&amp;quot;: 0xFF (EESAVE unprogrammed = 1). Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Eine besondere Funktion des avr-gcc ist, dass mit entsprechenden Optionen im Makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Variable auf feste Adressen legen ===&lt;br /&gt;
&lt;br /&gt;
Gleich zu Beginn möchte ich darauf hinweisen, dass dieses Verfahren nur ein Workaround ist, mit dem man das Problem der anscheinend &amp;quot;zufälligen&amp;quot; Verteilung&lt;br /&gt;
der EEPROM-Variablen durch den Compiler etwas in den Griff bekommen kann.&lt;br /&gt;
&lt;br /&gt;
Hilfreich kann dies vor allem dann sein, wenn man z.B. über einen Kommandointerpreter (o.ä. Funktionen) direkt bestimmte EEPROM-Adressen manipulieren möchte. Auch wenn man über einen JTAG-Adapter (mk I oder mkII) den Programmablauf manipulieren möchte, indem man die EEPROM-Werte direkt ändert, kann diese Technik hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Im folgenden nun zwei Sourcelistings mit einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.h&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#inlcude &amp;lt;avr/eeprom.h&amp;gt;     // Die EEPROM-Definitionen/Macros der avr-libc einbinden&lt;br /&gt;
&lt;br /&gt;
#define   EESIZE   512      // Maximale Größe des EEPROMS&lt;br /&gt;
&lt;br /&gt;
#define   EE_DUMMY   0x000  // Dummyelement (Adresse 0 sollte nicht genutzt werden)&lt;br /&gt;
#define   EE_VALUE1  0x001  // Eine Bytevariable  &lt;br /&gt;
#define   EE_WORD1L  0x002  // Eine Wordvariable (Lowbyte)&lt;br /&gt;
#define   EE_WORD1H  0x003  // Eine Wordvariable (Highbyte)&lt;br /&gt;
#define   EE_VALUE2  0x004  // Eine weitere Bytevariable&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den Macros &#039;&#039;&#039;#define EE_VALUE1&#039;&#039;&#039; legt man den Namen und die Adresse der&lt;br /&gt;
&#039;Variablen&#039; fest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Die Adressen sollten fortlaufend, zumindest aber aufsteigend sortiert sein! Ansonsten besteht die Gefahr, daß man sehr schnell ein Durcheinander im EEPROM Speicher veranstaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Für den Compiler sind das lediglich Speicher-Adressen, über die auf das EEPROM zugegriffen wird. Der Compiler sieht nichts davon als eine echte Variable an und stößt sich daher auch nicht daran, wenn 2 Makros mit der gleichen Speicheradresse, bzw. überlappenden Speicherbereichen definiert werden. Es liegt einzig und alleine in der Hand des Programmierers, hier keinen Fehler zu machen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
   [EE_DUMMY]   = 0x00,&lt;br /&gt;
   [EE_VALUE1]  = 0x05,&lt;br /&gt;
   [EE_WORD1L]  = 0x01,   &lt;br /&gt;
   [EE_WORD1H]  = 0x00,&lt;br /&gt;
   [EE_VALUE2]  = 0xFF&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch die Verwendung eines Array, welches das gesammte EEPROM umfasst, bleibt&lt;br /&gt;
dem Compiler nicht anderes übrig, als das Array so zu platzieren, dass Element 0&lt;br /&gt;
des Arrays der Adresse 0 des EEPROMs entspricht. (&#039;&#039;Ich hoffe nur, dass die Compilerbauer daran nichts ändern!&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Wie man in dem obigen Codelisting auch sehen kann, hat das Verfahren einen kleinen Haken. Variablen die größer sind als 1 Byte, müssen etwas umständlicher&lt;br /&gt;
definiert werden. Benötigt man keine Initialisierung durch das Programm (was der Normalfall sein dürfte), dann kann man das auch so machen:&lt;br /&gt;
&lt;br /&gt;
Möchte man im EEPROM hintereinander beispielsweise Variablen, mit den Namen &#039;&#039;&#039;Wert&#039;&#039;&#039;, &#039;&#039;&#039;Anzahl&#039;&#039;&#039;, &#039;&#039;&#039;Name&#039;&#039;&#039; und &#039;&#039;&#039;Wertigkeit&#039;&#039;&#039; definieren, wobei Wert und Wertigkeit 1 Byte belegen sollen, Anzahl als 1 Wort (also 2 Bytes) und Name mit 10 Bytes reserviert werden soll, so geht auch folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes Makro definiert also seine Startadresse durch die Startadresse der unmittelbar vorhergehende &#039;Variablen&#039; plus der Anzahl der Bytes die von der vorhergehenden &#039;Variablen&#039; verbraucht werden. Dadurch ist man zumindest etwas auf der sicheren Seite, dass keine 2 &#039;Variablen&#039; im EEPROM überlappend definiert werden. Möchte man eine weitere &#039;Variable&#039; hinzufügen, so wird deren&lt;br /&gt;
Name, einfach anstelle der EE_LAST eingesetzt und eine neue Zeile für EE_LAST eingefügt, in der dann die Größe der &#039;Variablen&#039; festgelegt wird. Zb.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_PROZENT    ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( double ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EE_PROZENT legt die Startadresse für eine neue &#039;Variable&#039; des Datentyps double fest.&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf die EEPROM Werte kann dann z.B.so erfolgen:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t   temp1;&lt;br /&gt;
uint16_t  temp2;&lt;br /&gt;
&lt;br /&gt;
temp1 = eeprom_read_byte(EE_VALUE1);&lt;br /&gt;
temp2 = eeprom_read_word(EE_WORD1L);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ob die in der avr-libc vorhandenen Funktionen dafür verwendet werden können, weiß ich nicht. Aber in einigen Fällen muss man sich sowieso eigene Funktionen&lt;br /&gt;
bauen, welche die spezifischen Anforderungen (Interrupt - Atom Problem, etc.)&lt;br /&gt;
erfüllen.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist nur eine Möglichkeit, wie man dies realisieren kann. Sie bietet einem eine relativ einfache Art die EEPROM-Werte&lt;br /&gt;
auf beliebige Adressen zu legen oder Adressen zu ändern. Die Andere Möglichkeit besteht darin, die EEPROM-Werte wie folgt zu belegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
  0x00,                     //  ee_dummy&lt;br /&gt;
  0x05,                     //  ee_value1&lt;br /&gt;
  0x01,                     //  ee_word1L&lt;br /&gt;
  0x00,                     // (ee_word1H)&lt;br /&gt;
  0xFF                      //  ee_value2&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei kann man Variablen, die größer sind als 1 Byte einfacher definieren und&lt;br /&gt;
man muss nur die Highbyte- oder Lowbyte-Adresse in der &amp;quot;eeprom.h&amp;quot; definieren.&lt;br /&gt;
Allerdings muss man hier höllisch aufpassen, dass man nicht um eine oder mehrere&lt;br /&gt;
Positionen verrutscht!&lt;br /&gt;
&lt;br /&gt;
Welche der beiden Möglichkeiten man einsetzt, hängt vor allem davon ab, wieviele&lt;br /&gt;
Byte, Word und sonstige Variablen man benutzt. Gewöhnen sollte man sich an beide Varianten können ;)&lt;br /&gt;
&lt;br /&gt;
Kleine Schlussbemerkung:&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc unterstützt die Variante 1 und die Variante 2&lt;br /&gt;
* Der icc-avr Compiler unterstützt nur die Variante 2!&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Bei alten Versionen der avr-libc wurden nicht alle AVR Controller  untersützt. Z.B. bei der avr-libc Version 1.2.3 insbesondere bei AVRs &amp;quot;der neuen Generation&amp;quot; (ATmega48/88/168/169) funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register). In neueren Versionen (z.B. avr-libc 1.4.3 aus WinAVR 20050125) wurde die Zahl der unterstüzten Controller deutlich erweitert und eine Methode zur leichten Anpassung an zukünftige Controller eingeführt.&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Will oder kann man nicht auf die neue Version aktualisieren, kann der dort gezeigte Code auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;, gegebenfalls Schutz vor Unterbrechnung/Interrupt ergänzen &#039;&#039;uint8_t sreg; sreg=SREG; cli(); [EEPROM-Code] ; SREG=sreg; return;&#039;&#039;, siehe Abschnitt Interrupts). Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== EEPROM Register ===&lt;br /&gt;
Um das EEPROM anzusteuern sind drei Register von Bedeutung.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEAR Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL da in einem 8 Bit Register keine 512 Adressen adressiert werden können&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEDR Hier werden die Daten eingetragen die geschrieben werden sollen bzw. es enthält die gelesenen Daten&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EECR Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TABLE BORDER=1&amp;gt;&lt;br /&gt;
&amp;lt;TR&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;Bit&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;1&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;0&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&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;Name&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&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;TD&amp;gt;-&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERIE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEMWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERE&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;Read/Write&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;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&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;Init Value&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;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;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;TD&amp;gt;0&amp;lt;/TD&amp;gt;&lt;br /&gt;
&amp;lt;/TR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/TABLE&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bedeutung der Bits:&amp;lt;/U&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4-7 nicht belegt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 3 (EERIE) - EEPROM Ready Interrupt Enable:&amp;lt;/U&amp;gt; Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7) wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0 wird kein Interrupt ausgelöst.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 2(EEMWE) - EEPROM Master Write Enable:&amp;lt;/U&amp;gt; Dieses Bit bestimmt, daß wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE =0 ist und EEWE = 1 gesetzt wird hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst.&lt;br /&gt;
Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 1 (EEWE) - EEPROM Write Enable:&amp;lt;/U&amp;gt; Dieses Bit löst den Schreibvorgang aus wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist wird dieses Bit automatisch wieder auf 0 gesetzt und sofern EERIE gesetzt ist ein Interrupt ausgelöst. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ein Schreibvorgang sieht typischerweise wie folgt aus:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. EEPROM Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Daten übergeben an EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1 &amp;lt;BR&amp;gt;&lt;br /&gt;
5. (Optinal) Warten bis Schreibvorgang abgeschlossen ist.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 0 EERE – EEPROM Read Enable:&amp;lt;/U&amp;gt; Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit wenn das Bit EEWE=0 ist. ISt der LEsevorgang abgeschlossen wird das Bit wieder auf 0 gesetzt und das EEPROM ist für neue Lese/Schreibbefehle wieder bereit.&amp;lt;BR&amp;gt;&lt;br /&gt;
Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. Bereitschaft zum lesen prüfen (EEWE=0)&amp;lt;BR&amp;gt;&lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Lesezyklus auslösen mit EERE = 1&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Warten bis Lesevorgang abgeschlossen EERE = 0&amp;lt;BR&amp;gt;&lt;br /&gt;
5. Daten abholen aus EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bietet sich printf/sprintf an. &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t s[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( s, &amp;quot;Zählerstand: %d&amp;quot;, value );&lt;br /&gt;
    uart_puts( s );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, die Ausgabe stdout auf eigene Ausgabefunktionen umzuleiten. Dazu wird dem Ausgabemechanismus eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben (wie auch immer die Ausgabe stattfindet). Alle anderen, höheren, Funktionen greifen letztendlich auf diese eine Ausgabefunktion zurück, die die Ausgabe eines einzelnen Zeichens vornimmt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uart_init();&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
    printf( &amp;quot;Hello, world!\n&amp;quot; );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil aller printf-Varianten: Sie sind recht speicherintensiv.&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C- und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gnu-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Inline-Assembler: Die Assembleranweisungen werden direkt in den C-Code integriert. Eine Quellcode-Datei enthält somit C- und Assembleranweisungen&lt;br /&gt;
* Assembler-Dateien: Der Assemblercode befindet sich in eigenen Quellcodedateien. Diese werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung (NOP steht für No Operation). Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=Warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
   /* Verzögern der weiteren Programmausführung um&lt;br /&gt;
      genau 3 Taktzyklen */&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeintlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
&lt;br /&gt;
    /* leere Schleife - wird bei eingeschalteter Compiler-Optimierung   wegoptimiert */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* alternative Methode (keine Optimierung): */&lt;br /&gt;
    volatile uint16_t j;&lt;br /&gt;
    for (j = 0; j &amp;lt; 1000; j++)&lt;br /&gt;
      ;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützlicher &amp;quot;Assembler-Einzeiler&amp;quot; ist der Aufruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;);&#039;&#039;), da hierzu in älteren Versionen der avr-libc keine eigene Funktion existiert (in neueren Versionen &#039;&#039;sleep_cpu()&#039;&#039; aus sleep.h).&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise Delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil, dass die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static inline void delayloop16 (uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
    asm volatile (&amp;quot;cp  %A0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;cpc %B0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;breq 2f               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;1:                    \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;sbiw %0,1             \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;brne 1b               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;2:                    &amp;quot;  &lt;br /&gt;
                  : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
	          : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
    );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Anweisung wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen. Der Zeilenumbruch teilt dem Assembler mit, dass ein neuer Befehl beginnt.&lt;br /&gt;
* Als Sprung-Marken (Labels) werden Ziffern verwendet. Diese speziellen Labels sind mehrfach im Code verwendbar. Gesprungen wird jeweils zurück (b) oder vorwärts (f) zum nächsten ausffindbaren Label.&lt;br /&gt;
&lt;br /&gt;
Das Resultat zeigt ein Blick in die Assembler-Datei, die der Compiler mit der option &amp;lt;tt&amp;gt;-save-temps&amp;lt;/tt&amp;gt; nicht löscht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	cp  r24, __zero_reg__ 	 ;  count&lt;br /&gt;
	cpc r25, __zero_reg__ 	 ;  count&lt;br /&gt;
	breq 2f               &lt;br /&gt;
	1:                    &lt;br /&gt;
	sbiw r24,1             	 ;  count&lt;br /&gt;
	brne 1b               &lt;br /&gt;
	2:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc im Abschnitt [http://www.nongnu.org/avr-libc/user-manual/inline_asm.html Related Pages/Inline Asm]. &lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc Deutsche Einführung in Inline-Assembler]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
Wenn man mit dem AVR Studio arbeitet, kann alternativ auch das standardmäßig erstellte Makefile bearbeitet und folgende Zeilen eingefügt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
## Objects that must be built in order to link&lt;br /&gt;
OBJECTS = (alte Dateien...) useful.o&lt;br /&gt;
&lt;br /&gt;
## Compile&lt;br /&gt;
## Hier folgt eine Liste der gelinkten Dateien, darunter einfügen:&lt;br /&gt;
useful.o: ../useful.S&lt;br /&gt;
	$(CC) $(INCLUDES) $(ASMFLAGS) -c  $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Das war es schon. Allerdings gilt es zu beachten, dass das makefile über &amp;quot;Project -&amp;gt; Configuration options&amp;quot; selbst einzubinden ist, sonst wird es natürlich wieder überschrieben.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel eine Funktion &#039;&#039;superFunc&#039;&#039;, die alle Pins des Ports D auf &amp;quot;Ausgang&amp;quot; schaltet, eine Funktion &#039;&#039;ultraFunc&#039;&#039;, die die Ausgänge entsprechend des übergebenen Parameters schaltet, eine Funktion &#039;&#039;gigaFunc&#039;&#039;, die den Status von Port A zurückgibt und eine Funktion &#039;&#039;addFunc&#039;&#039;, die zwei Bytes zu einem 16-bit-Wort addiert. Die Zuweisungen im C-Code (PORTx = ...) verhindern, dass der Compiler die Aufrufe wegoptimiert und dienen nur zur Veranschaulichung der Parameterübergaben.&lt;br /&gt;
&lt;br /&gt;
Zuerst der Assembler-Code. Der Dateiname sei useful.S:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//; Arbeitsregister (ohne &amp;quot;r&amp;quot;) &lt;br /&gt;
workreg  = 16&lt;br /&gt;
workreg2 = 17&lt;br /&gt;
&lt;br /&gt;
//; Konstante:&lt;br /&gt;
ALLOUT = 0xff&lt;br /&gt;
&lt;br /&gt;
//; ** Setze alle Pins von PortD auf Ausgang **&lt;br /&gt;
//; keine Parameter, keine Rückgabe&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg  // beachte: _SFR_IO_ADDR()&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Setze PORTD auf übergebenen Wert **&lt;br /&gt;
//; Parameter in r24 (LSB immer bei &amp;quot;graden&amp;quot; Nummern)&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zustand von PINA zurückgeben **&lt;br /&gt;
//; Rückgabewerte in r24:r25 (LSB:MSB), hier nur LSB genutzt&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zwei Bytes addieren und 16-bit-Wort zurückgeben **&lt;br /&gt;
//; Parameter in r24 (Summand1) und r22 (Summand2) -&lt;br /&gt;
//;  Parameter sind Word-&amp;quot;aligned&amp;quot; d.h. LSB immer auf &amp;quot;graden&amp;quot;&lt;br /&gt;
//;  Registernummern. Bei 8-Bit und 16-Bit Paramtern somit &lt;br /&gt;
//;  beginnend bei r24 dann r22 dann r20 etc.&lt;br /&gt;
//; Rückgabewert in r24:r25&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   push workreg2&lt;br /&gt;
   clr workreg2&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
   pop workreg2&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
//; oh je - sorry - Mein AVR-Assembler ist eingerostet, hoffe das stimmt so...&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Makefile ist der Name der Assembler-Quellcodedatei einzutragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
ASRC = useful.S&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Aufruf erfolgt dann im C-Code so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
extern void superFunc(void);&lt;br /&gt;
extern void ultraFunc(uint8_t setVal);&lt;br /&gt;
extern uint8_t gigaFunc(void);&lt;br /&gt;
extern uint16_t addFunc(uint8_t w1, uint8_t w2);&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
[...]&lt;br /&gt;
  superFunc();&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
&lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
[...]&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird wieder in der lss-Datei ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
   superFunc();&lt;br /&gt;
 148:	0e 94 f6 00 	call	0x1ec&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
 14c:	85 e5       	ldi	r24, 0x55	; 85&lt;br /&gt;
 14e:	0e 94 fb 00 	call	0x1f6&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
 152:	0e 94 fd 00 	call	0x1fa&lt;br /&gt;
 156:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
  &lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
 158:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 15a:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 15c:	0e 94 ff 00 	call	0x1fe&lt;br /&gt;
 160:	8b bb       	out	0x1b, r24	; 27&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
 162:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 164:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 166:	0e 94 fc 00 	call	0x1f8&lt;br /&gt;
 16a:	89 2f       	mov	r24, r25&lt;br /&gt;
 16c:	99 27       	eor	r25, r25&lt;br /&gt;
 16e:	88 bb       	out	0x18, r24	; 24&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
000001ec &amp;lt;superFunc&amp;gt;:&lt;br /&gt;
// setze alle Pins von PortD auf Ausgang&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1ec:	0f 93       	push	r16&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
 1ee:	0f ef       	ldi	r16, 0xFF	; 255&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg&lt;br /&gt;
 1f0:	01 bb       	out	0x11, r16	; 17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 1f2:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 1f4:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001f6 &amp;lt;ultraFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// setze PORTD auf übergebenen Wert&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
 1f6:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
   ret&lt;br /&gt;
 1f8:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fa &amp;lt;gigaFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Zustand von PINA zurückgeben&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
 1fa:	89 b3       	in	r24, 0x19	; 25&lt;br /&gt;
   ret&lt;br /&gt;
 1fc:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fe &amp;lt;addFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// zwei Bytes addieren und 16-bit-Wort zurückgeben&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1fe:	0f 93       	push	r16&lt;br /&gt;
   push workreg2&lt;br /&gt;
 200:	1f 93       	push	r17&lt;br /&gt;
   clr workreg2&lt;br /&gt;
 202:	11 27       	eor	r17, r17&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
 204:	06 2f       	mov	r16, r22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
 206:	08 0f       	add	r16, r24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
 208:	11 1d       	adc	r17, r1&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
 20a:	c8 01       	movw	r24, r16&lt;br /&gt;
   pop workreg2&lt;br /&gt;
 20c:	1f 91       	pop	r17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 20e:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 210:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zuweisung von Registern zu Parameternummer und die Register für die Rückgabewerte sind in den &amp;quot;Register Usage Guidelines&amp;quot; der avr-libc-Dokumentation erläutert.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/assembler.html avr-libc-Dokumentation: Related Pages/avr-libc and assembler programs]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage avr-libc-Dokumentation: Related Pages/FAQ/&amp;quot;What registers are used by the C compiler?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
== Globale Variablen für Datenaustausch ==&lt;br /&gt;
&lt;br /&gt;
Oftmals kommt man um globale Variablen nicht herum, z.B. um den Datenaustausch zwischen Hauptprogramm und Interrupt-Routinen zu realisieren. &lt;br /&gt;
Hierzu muss man im Assembler wissen, wo genau die Variable vom C-Compiler abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
Hierzu muss die Variable, hier &amp;quot;zaehler&amp;quot; genannt, zuerst im C-Code als Global definiert werden, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
int16_t main (void)&lt;br /&gt;
{&lt;br /&gt;
    // irgendein Code, in dem zaehler benutzt werden kann&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im foldenden Assembler-Beispiel wird der Externe Interrupt0  verwendet, um den Zähler hochzuzählen. Es fehlen die Initialisierungen des Interrupts und die Interrupt-Freigabe, so richtig sinnvoll ist der Code auch nicht, aber er zeigt (hoffentlich) wie es geht.&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit Interrupt-Vektoren gilt beim GCC-Assembler das Gleiche, wie bei C: Man muss die exakte Schreibweise beachten, ansonsten wird nicht der Interrupt-Vektor angelegt, sondern eine neue Funktion - und man wundert sich, dass nichts funktionert (vgl. das AVR-GCC-Handbuch).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
.extern zaehler&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      //; wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     //; Status-Register (SREG) sichern!&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     lds temp,zaehler               //; Wert aus dem Speicher lesen&lt;br /&gt;
     inc temp                       //; bearbeiten&lt;br /&gt;
     sts zaehler,temp               //; und wieder zurückschreiben&lt;br /&gt;
&lt;br /&gt;
     pop temp                       //; die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen im Assemblerfile anlegen ===&lt;br /&gt;
&lt;br /&gt;
Alternativ können Variablen aber auch im Assemblerfile angelegt werden. Dadurch kann auf eine .c-Datei verzichtet werden. Für das obige Beispiel könnte der Quelltext dann die Dateien zaehl_asm.S und zaehl_asm.h abgelegt werden, so dass nur noch zaehl_asm.S mit kompiliert werden müsste.&lt;br /&gt;
&lt;br /&gt;
Anstatt im Assemblerfile über das Schlüsselwort &#039;&#039;.extern &#039;&#039; auf eine vorhandene Variable zu verweisen, wird dazu mit dem Schlüsselwort &#039;&#039;.comm&#039;&#039; die benötigte Anzahl von Bytes für eine Variable reserviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.S&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
//; 1 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 1&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Headerdatei wird dann auf die Variable nur noch verwiesen (Schlüsselwort &#039;&#039;extern&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef ZAEHL_ASM_H&lt;br /&gt;
#define ZAEHL_ASM_H&lt;br /&gt;
&lt;br /&gt;
extern volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu globalen Variablen in C werden so angelegte Variablen nicht automatisch mit dem Wert 0 initialisiert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer als 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Variablen, die größer als &#039;&#039;&#039;ein&#039;&#039;&#039; Byte sind, können in Assembler auf ähnliche Art angesprochen werden. Hierzu müssen nur genug Bytes angefordert werden, um die Variable aufzunehmen. Soll z.B. für den Zähler eine Variable vom Typ &#039;&#039;unsigned long&#039;&#039;, also &#039;&#039;uint32_t&#039;&#039; verwendet werden, so müssen 4 Bytes reserviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die dazugehörige Deklaration im Headerfile wäre dann:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
extern volatile uint32_t zaehler;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen, die größer als ein Byte sind, werden die Werte beginnend mit dem niederwertigsten Byte im RAM abgelegt. Das folgende Codeschnippsel zeigt, wie unter Assembler auf die einzelnen Bytes zugegriffen werden kann. Dazu wird im Interrupt nun ein 32-Bit Zähler erhöht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      // wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     // Status-Register (SREG) sichern !&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     // 32-Bit-Zähler incrementieren&lt;br /&gt;
     lds temp, (zaehler + 0)        // 0. Byte (niederwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 0), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
     lds temp, (zaehler + 1)        // 1. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 1), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 2)        // 2. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 2), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 3)        // 3. Byte (höchstwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 3), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
RAUS:&lt;br /&gt;
     pop temp                       // die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TODO:&#039;&#039;&#039; 16-Bit / 32-Bit Variablen, Zugriff auf Arrays (Strings)&lt;br /&gt;
&lt;br /&gt;
= Anhang =&lt;br /&gt;
&lt;br /&gt;
== Besonderheiten bei der Anpassung bestehenden Quellcodes ==&lt;br /&gt;
&lt;br /&gt;
Einige Funktionen, die in frühren Versionen der avr-libc vorhanden waren, werden inzwischen als veraltet angesehen. Sie sind nicht mehr vorhanden oder als &#039;&#039;deprecated&#039;&#039; (missbilligt) ausgewiesen und Definitionen in &amp;lt;compat/deprecated.h&amp;gt; verschoben. Es empfiehlt sich, vorhandenen Code zu portieren und die alten Funktionen nicht mehr zu nutzen, auch wenn diese noch zur Verfügung stehen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zur Deklaration von Interrupt-Routinen ===&lt;br /&gt;
&lt;br /&gt;
Die Funktionen (eigentlich Makros) &#039;&#039;SIGNAL&#039;&#039; und &#039;&#039;INTERRUPT&#039;&#039; zur Deklaration von Interruptroutinen sollten nicht mehr genutzt werden. &lt;br /&gt;
&lt;br /&gt;
In aktuellen Versionen der avr-libc (z.B. avr-libc 1.4.3 aus WinAVR 20060125) werden Interruptroutinen, die &#039;&#039;&#039;nicht&#039;&#039;&#039; durch andere Interrupts &#039;&#039;&#039;unterbrechbar&#039;&#039;&#039; sind, mit ISR deklariert (siehe Abschnitt im Hauptteil). Auch die Benennung wurden vereinheitlicht und an die üblichen Bezeichnungen in den AVR Datenblättern angepasst. In der Dokumentation der avr-libc sind alte und neue Bezeichnungen in der Tabelle gegenübergestellt. Die erforderlichen Schritte zur Portierung:&lt;br /&gt;
&lt;br /&gt;
* #include von avr/signal.h entfernen&lt;br /&gt;
* SIGNAL duch ISR ersetzen&lt;br /&gt;
* Name des Interrupt-Vektors anpassen (SIG_* durch entsprechendes *_vect)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für die Anpassung zuerst ein &amp;quot;alter&amp;quot; Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer2 Output Compare bei einem ATmega8 */&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE2)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt wird der Vektor mit TIMER2 COMP bezeichnet. Die Bezeichnung in der avr-libc entspricht dem Namen im Datenblatt, Leerzeichen werden durch Unterstriche (_) ersetzt und ein _vect angehängt. &lt;br /&gt;
&lt;br /&gt;
Der neue Code sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt; &lt;br /&gt;
/* signal.h entfällt */&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER2_COMP_vect)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Unklarheiten bezüglich der neuen Vektorlabels hilft (noch) ein Blick in die Headerdatei des entsprechenden Controllers. Für das vorherige Beispiel also der Blick in die Datei iom8.h für den ATmega8, dort findet man die veraltete Bezeichnung unterhalb der aktuellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect		_VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2		_VECTOR(3)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Nachfolgendes mit avr-libc 1.4.5 (in WinAVR 1/2007 behoben - noch eine Weile auskommentiert lassen und dann löschen.&lt;br /&gt;
Konnte in alten Versionen signal.h ohne interrupt.h eingebunden werden, erhält man bei Verwendung der avr-libc Version 1.4.3 (WinAVR 2/2005) beim Compilieren eine Fehlermeldung, da mit signal.h nicht die erforderlichen Definitionen eingebunden werden. Der Lösungsvorschlag in signal.h auch interrupt.h einzubinden, wurde von den avr-libc-Enwicklern akzeptiert und das Problem ist  im Quellcode (CVS) bereits behoben. Es ist aber noch keine avr-libc-&amp;quot;Release&amp;quot; bzw. noch kein WinAVR mit dieser avr-libc-Korrektur verfügbar (Stand 5.2.2006). Will oder kann man den Quellcode nicht aktualisieren, gibt es folgende Alternativen:&lt;br /&gt;
* in Quellcodedateien, in denen nur avr/signal.h eingebunden wird, interrupt.h einbinden (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;). signal.h weiterhin einbinden, falls Kompatibiltät mit alten Versionen gewünscht ist.&lt;br /&gt;
* in der Datei signal.h (bein WinAVR in c:/WinAVR/avr/include/avr/signal.h) ein (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;) ergänzen --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;unterbrechbare&#039;&#039;&#039; Interruptroutinen, die mittels &#039;&#039;INTERRUPT&#039;&#039; deklariert sind, gibt es keinen direkten Ersatz in Form eines Makros. Solche Routinen sind laut Dokumentation der avr-libc in folgender Form zu deklarieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* ** alt ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
INTERRUPT(SIG_OVERFLOW0)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ** neu: ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void TIMER0_OVF_vect(void) __attribute__((interrupt));&lt;br /&gt;
void TIMER0_OVF_vect(void) &lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von &#039;&#039;INTERRUPT&#039;&#039; die Header-Datei &#039;&#039;compat/deprecated.h&#039;&#039; einzubinden. Man sollte bei dieser Gelegenheit jedoch nochmals überprüfen, ob die Funktionalität von &#039;&#039;INTERRUPT&#039;&#039; tatsächlich gewollt ist. In vielen Fällen wurde &#039;&#039;INTERRUPT&#039;&#039; dort genutzt, wo eigentlich &#039;&#039;SIGNAL&#039;&#039; (nunmehr &#039;&#039;ISR&#039;&#039;) hätte genutzt werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;inp&#039;&#039; und &#039;&#039;outp&#039;&#039; zum Einlesen bzw. Schreiben von Registern sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
unsigned char i, j;&lt;br /&gt;
&lt;br /&gt;
// alt:&lt;br /&gt;
  i = inp(PINA);&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  outp(PORTB, j);&lt;br /&gt;
&lt;br /&gt;
// neu (nicht mehr wirklich neu...):&lt;br /&gt;
  i = PINA&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  PORTB = j;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von inp und outp die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Zugriff auf Bits in Registern ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;cbi&#039;&#039; und &#039;&#039;sbi&#039;&#039; zum Löschen und Setzen von Bits sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg. Die Bezeichnung ist ohnehin irreführend da die Funktionen nur für Register mit Adressen im unteren Speicherbereich tatsächlich in die Assembleranweisungen cbi und sbi übersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// alt:&lt;br /&gt;
  sbi(PORTB, PB2);&lt;br /&gt;
  cbi(PORTC, PC1);&lt;br /&gt;
&lt;br /&gt;
// neu (auch nicht mehr wirklich neu...):&lt;br /&gt;
  PORTB |=  (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
  PORTC &amp;amp;= ~(1&amp;lt;&amp;lt;PC1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von sbi und cbi die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden. Wer unbedingt will, kann sich natürlich eigene Makros mit aussagekräftigeren Namen definieren. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &amp;amp;= ~(1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Selbstdefinierte (nicht-standardisierte) ganzzahlige Datentypen ===&lt;br /&gt;
&lt;br /&gt;
Bei den im Folgenden genannten Typdefinitionen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich, bei der Überarbeitung von altem Code die im Abschnitt &#039;&#039;standardisierten ganzzahligen Datentypen&#039;&#039; beschriebenen Datentypen zu nutzen (stdint.h) und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef unsigned char      BYTE;       // besser: uint8_t  aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned short     WORD;       // besser: uint16_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long      DWORD;      // besser: uint32_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long long QWORD;      // besser: uint64_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; BYTE : Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 255. &lt;br /&gt;
&lt;br /&gt;
; WORD : Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 65535. &lt;br /&gt;
&lt;br /&gt;
; DWORD : Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
; QWORD : Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Funktionen im Makefile ==&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken (Libraries/.a-Dateien) hinzufügen ===&lt;br /&gt;
&lt;br /&gt;
Um Funktionen aus Bibliotheken (&amp;quot;echte&amp;quot; Libraries, *.a-Dateien) zu nutzen, sind dem Linker die Namen der Bibliotheken als Parameter zu übergeben. Dazu ist die Option -l (kleines L) vorgesehen, an die der Name der Library angehängt wird. &lt;br /&gt;
&lt;br /&gt;
Dabei ist zu beachten, dass der Name der Library und der Dateiname der Library nicht identisch sind. Der hinter -l angegebene Name entspricht dem Dateinamen der Library ohne die Zeichenfolge &#039;&#039;lib&#039;&#039; am Anfang des Dateinamens und ohne die Endung &#039;&#039;.a&#039;&#039;. Sollen z.B. Funktionen aus einer Library mit dem Dateinamen &#039;&#039;libefsl.a&#039;&#039; eingebunden (gelinkt) werden, lautet der entsprechende Parameter -lefsl (vergl. auch -lm zum Anbinden von libm.a). &lt;br /&gt;
&lt;br /&gt;
In Makefiles wird traditonell eine make-Variable LDLIBS genutzt, in die &amp;quot;l-Parameter&amp;quot; abgelegt werden. Die WinAVR-makefile-Vorlage enthält diese Variable zwar nicht, dies stellt jedoch keine Einschränkung dar, da alle in der make-Variable LDFLAGS abgelegten Parameter an den Linker weitergereicht werden. &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Einbinden von Funktionen aus einer Library efsl (Dateiname libefsl.a)&lt;br /&gt;
LDFLAGS += -lefsl&lt;br /&gt;
# Einbinden von Funktionen aus einer Library xyz (Dateiname libxyz.a)&lt;br /&gt;
LDFLAGS += -lxyz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Liegen die Library-Dateien nicht im Standard Library-Suchpfad, sind die Pfade mittels Parameter &#039;&#039;-L&#039;&#039; ebenfalls anzugeben. (Der vordefinierte Suchpfad kann mittels &#039;&#039;avr-gcc --print-search-dirs&#039;&#039; angezeigt werden.)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Projekt (&amp;quot;superapp2&amp;quot;), in dem der Quellcode von zwei Libraries (efsl und xyz) und der Quellcode der eigentlichen Anwendung in verschiedenen Verzeichnissen mit der folgenden &amp;quot;Baumstruktur&amp;quot; abgelegt sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
superapp2&lt;br /&gt;
|&lt;br /&gt;
+----- efslsource (darin libefsl.a)&lt;br /&gt;
|&lt;br /&gt;
+----- xyzsource (darin libxyz.a)&lt;br /&gt;
|&lt;br /&gt;
+----- firmware (darin Anwendungs-Quellcode und Makefile)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt, dass im Makefile die Verzeichnis efslsource und xyzsource in den Library-Suchpfad aufzunehmen sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
LDFLAGS += -L../efslsource/ -L../xyzsource/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fuse-Bits ===&lt;br /&gt;
&lt;br /&gt;
Zur Berechnung der Fuse-Bits bietet sich neben dem Studium des Datenblattes auch der [http://palmavr.sourceforge.net/cgi-bin/fc.cgi AVR Fuse Calculator] an. Gewarnt werden muss vor der Benutzung von PonyProg, weil dort durch die negierte Darstellung gern Fehler gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Soll die Programmierung von Fuse- und Lockbits automatisiert werden, kann man dies ebenfalls durch Einträge im Makefile vornehmen, die beim Aufruf von &amp;quot;make program&amp;quot; an die genutzte Programmiersoftware übergeben werden. In der makefile-Vorlage von WinAVR (und mfile) gibt es dafuer jedoch keine &amp;quot;Ausfüllhilfe&amp;quot; (Stand 9/2006). Die folgenden Ausführungen gelten für die Programmiersoftware [[AVRDUDE]] (Standard in der WinAVR-Vorlage), können jedoch sinngemäß auf andere Programmiersoftware übertragen werden, die die Angabe der Fuse- und Lockbits-Einstellungen per Kommandozeilenparameter unterstützt (z.B. stk500.exe). Im einfachsten Fall ergänzt man im Makefile einige Variablen, deren Werte natürlich vom verwendeten Controller und den gewünschten Einstellungen abhängen (vgl. Datenblatt Fuse-/Lockbits):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#---------------- Programming Options (avrdude) ----------------&lt;br /&gt;
&lt;br /&gt;
#...&lt;br /&gt;
#Beispiel! f. ATmega16 - nicht einfach uebernehmen! Zahlenwerte anhand&lt;br /&gt;
#--------- des Datenblatts nachvollziehen und gegebenenfalls aendern.&lt;br /&gt;
#&lt;br /&gt;
AVRDUDE_WRITE_LFUSE = -U lfuse:w:0xff:m&lt;br /&gt;
AVRDUDE_WRITE_HFUSE = -U hfuse:w:0xd8:m&lt;br /&gt;
AVRDUDE_WRITE_LOCK  = -U lock:w:0x2f:m&lt;br /&gt;
#...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit diese Variablen auch genutzt werden, ist der Aufruf von avrdude im Makefile entsprechend zu ergänzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Program the device.  &lt;br /&gt;
program: $(TARGET).hex $(TARGET).eep&lt;br /&gt;
# ohne Fuse-/Lock-Einstellungen (nach WinAVR Vorlage Stand 4/2006)&lt;br /&gt;
#	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
#        $(AVRDUDE_WRITE_EEPROM)&lt;br /&gt;
# mit Fuse-/Lock-Einstellungen&lt;br /&gt;
        $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_LFUSE) \&lt;br /&gt;
        $(AVRDUDE_WRITE_HFUSE) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
        $(AVRDUDE_WRITE_EEPROM) $(AVRDUDE_WRITE_LOCK)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weiter Möglichkeit besteht darin, die Fuse- und Lockbit-Einstellungen vom Preprozessor/Compiler generieren zu lassen. Die Fuse-Bits werden dann bei Verwendung von AVRDUDE in eigene Hex-Files geschrieben. Hierzu kann man z.B. folgendes Konstrukt verwenden:&lt;br /&gt;
&lt;br /&gt;
In eine der C-Sourcen wird eine Variable je Fuse-Byte vom Typ &#039;&#039;unsigned char&#039;&#039; deklariert und in eine extra Section gepackt. Dies kann entweder in einem vorhandenen File passieren oder in ein neues (z.B. fuses.c) geschrieben werden. Das File muss im Makefile aber auf jeden Fall mit kompiliert und gelinkt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// tiny 2313 fuses low byte&lt;br /&gt;
#define CKDIV8  7&lt;br /&gt;
#define CKOUT   6&lt;br /&gt;
#define SUT1    5&lt;br /&gt;
#define SUT0    4&lt;br /&gt;
#define CKSEL3  3&lt;br /&gt;
#define CKSEL2  2&lt;br /&gt;
#define CKSEL1  1&lt;br /&gt;
#define CKSEL0  0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses high byte&lt;br /&gt;
#define DWEN       7&lt;br /&gt;
#define EESAVE     6&lt;br /&gt;
#define SPIEN      5&lt;br /&gt;
#define WDTON      4&lt;br /&gt;
#define BODLEVEL2  3&lt;br /&gt;
#define BODLEVEL1  2&lt;br /&gt;
#define BODLEVEL0  1&lt;br /&gt;
#define RSTDISBL   0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses extended byte&lt;br /&gt;
#define SELFPRGEN  0&lt;br /&gt;
&lt;br /&gt;
#define LFUSE         __attribute__ ((section (&amp;quot;lfuses&amp;quot;)))&lt;br /&gt;
#define HFUSE         __attribute__ ((section (&amp;quot;hfuses&amp;quot;)))&lt;br /&gt;
#define EFUSE         __attribute__ ((section (&amp;quot;efuses&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// select ext crystal 3-8Mhz&lt;br /&gt;
unsigned char lfuse LFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;CKDIV8) | (1&amp;lt;&amp;lt;CKOUT) | (1&amp;lt;&amp;lt;CKSEL3) | (1&amp;lt;&amp;lt;CKSEL2) | &lt;br /&gt;
      (0&amp;lt;&amp;lt;CKSEL1) | (1&amp;lt;&amp;lt;CKSEL0) | (0&amp;lt;&amp;lt;SUT1) | (1&amp;lt;&amp;lt;SUT0) );&lt;br /&gt;
unsigned char hfuse HFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;DWEN) | (1&amp;lt;&amp;lt;EESAVE) | (0&amp;lt;&amp;lt;SPIEN) | (1&amp;lt;&amp;lt;WDTON) | &lt;br /&gt;
      (1&amp;lt;&amp;lt;BODLEVEL2) | (1&amp;lt;&amp;lt;BODLEVEL1) | (0&amp;lt;&amp;lt;BODLEVEL0) | (1&amp;lt;&amp;lt;RSTDISBL) );&lt;br /&gt;
unsigned char efuse EFUSE =&lt;br /&gt;
    ((0&amp;lt;&amp;lt;SELFPRGEN));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG: Die Bitpositionen wurden nicht vollständig getestet!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Eine &amp;quot;1&amp;quot; bedeutet hier, dass das Fuse-Bit &#039;&#039;nicht&#039;&#039; programmiert wird - die Funktion also i.A. nicht aktiviert ist. Eine &amp;quot;0&amp;quot; hingegen aktiviert die meisten Funktionen. Dies ist wie im Datenblatt (1 = unprogrammed, 0 = programmed).&lt;br /&gt;
&lt;br /&gt;
Das Makefile muss nun noch um folgende Targets erweitert werden (mit Tabulator einrücken - nicht mit Leerzeichen):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
lfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j lfuses --change-section-address lfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-lfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-lfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U lfuse:w:$(TARGET)-lfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
hfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j hfuses --change-section-address hfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-hfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-hfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U hfuse:w:$(TARGET)-hfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
efuses: build&lt;br /&gt;
        -$(OBJCOPY) -j efuses --change-section-address efuses=0 \&lt;br /&gt;
         -O ihex $(TARGET).elf $(TARGET)-efuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-efuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U efuse:w:$(TARGET)-efuse.hex;&lt;br /&gt;
        fi;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Target &amp;quot;clean&amp;quot; muss noch um die Zeilen&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
        $(REMOVE) $(TARGET)-lfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-hfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-efuse.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
erweitert werden, wenn auch die Fuse-Dateien gelöscht werden sollen.&lt;br /&gt;
&lt;br /&gt;
Um nun die Fusebits des angeschlossenen Controllers zu programmieren muss lediglichein &amp;quot;make lfuses&amp;quot;, &amp;quot;make hfuses&amp;quot; oder &amp;quot;make efuses&amp;quot; gestartet werden.&lt;br /&gt;
Bei den Fuse-Bits ist besondere Vorsicht geboten, da diese das Programmieren des Controllers unmöglich machen können. Also erst programmieren, wenn man einen HV-Programmierer hat oder ein paar Reserve-AVRs zur Hand ;-)&lt;br /&gt;
&lt;br /&gt;
Um weiterhin den &amp;quot;normalen&amp;quot; Flash beschreiben zu können, ist es wichtig, für das Target &amp;quot;*.hex&amp;quot; im Makefile nicht nur &amp;quot;-R .eeprom&amp;quot; als Parameter zu übergeben sondern zusätzlich noch &amp;quot;-R lfuses -R efuses -R hfuses&amp;quot;. Sonst bekommt AVRDUDE Probleme diese Sections in den Flash (wo sie ja nicht hingehören) zu schreiben.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[AVR_Fuses#Vergleich_der_Fuses_bei_verschiedenen_Programmen|Vergleich der Fuses bei verschiedenen Programmen]]&lt;br /&gt;
&lt;br /&gt;
== Externe Referenzspannung des internen Analog-Digital-Wandlers ==&lt;br /&gt;
&lt;br /&gt;
Die minimale (externe) Referenzspannung des ADC darf nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. z.B. beim ATMEGA8 darf sie laut Datenblatt (S.245, Tabelle 103, Zeile &amp;quot;VREF&amp;quot;) 2,0V nicht unterschreiten. HINWEIS: diese Information findet sich erst in der letzten Revision (Rev. 2486O-10/04) des Datenblatts.&lt;br /&gt;
&lt;br /&gt;
Meiner &amp;lt;!-- Wer? - es gibt inzwischen x Leute die mehr oder weniger viel in diesem Artikel geschrieben haben --&amp;gt; eigenen Erfahrung nach kann man aber (auf eigene Gefahr und natürlich nicht für Seriengeräte) durchaus noch ein klein wenig weiter heruntergehen, bei dem von mir unter die Lupe genommenen ATMEGA8L (also die Low-Voltage-Variante) funktioniert der ADC bei 5V Betriebsspannung mit bis zu VREF=1,15V hinunter korrekt, ab 1,1V und darunter digitalisiert er jedoch nur noch Blödsinn). Ich würde sicherheitshalber nicht unter 1,5V gehen und bei niedrigeren Betriebsspannungen mag sich die Untergrenze für VREF am Pin AREF ggf. nach oben&#039;&#039;&#039;(!)&#039;&#039;&#039; verschieben.&lt;br /&gt;
&lt;br /&gt;
In der letzten Revision des Datenblatts ist außerdem korrigiert, dass ADC4 und ADC5 sehr wohl 10 Bit Genauigkeit bieten (und nicht bloß 8 Bit, wie in älteren Revisionen irrtümlich angegeben.)&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* stdio.h, malloc() &lt;br /&gt;
* Code-Optimierungen (&amp;quot;tricks&amp;quot;), siehe auch Application Note [http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf AVR035: Efficient C Coding for AVR]&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
* SPI siehe [http://www.uni-koblenz.de/~physik/informatik/MCU/SPI.pdf SPI Bus mit Atmel AVR]&lt;br /&gt;
* I²C / TWI Bus [http://www.roboternetz.de/wissen/index.php/TWI]&lt;br /&gt;
* Bootloader (bez. auf boot.h)&lt;br /&gt;
* CAN-Bus&lt;br /&gt;
* Einsatz von einfachen Betriebssystemen auf dem AVR&lt;br /&gt;
* Übersicht zu den C bzw. GCC-predefined Makros (__DATE__, __TIME__,...)&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
* ADC ; &lt;br /&gt;
* Timer&lt;br /&gt;
* USB ; Steuerung mit USB&lt;br /&gt;
* Multiplexen Siebensegment&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28252</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28252"/>
		<updated>2008-05-27T17:02:38Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Exkurs: Makefiles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[AVR-GCC]] erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Progammiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong]. Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern, weder in Assembler noch in einer anderen Sprache. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR Controllern finden. Bei WinAVR gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden stetig weiterentwickelt. Erläuterungen und Beispiele beziehen sich auf den C-Compiler avr-gcc ab Version 3.4 und die avr-libc ab Version 1.4.3. Die Unterschiede zu älteren Versionen werden im Haupttext und Anhang zwar erläutert, Anfängern sei jedoch empfohlen, die aktuellen Versionen zu nutzen (für MS-Windows: aktuelle Version des [[WinAVR]]-Pakets). &lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in PDF-Form erhältlich bei:&lt;br /&gt;
http://www.siwawi.arubi.uni-kl.de/avr_projects/AVR-GCC-Tutorial_-_www_mikrocontroller_net.pdf&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels avr-gcc/avr-libc zu erstellen und zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR Controllers, der vom avr-gcc Compiler unterstützt wird (alle ATmegas und die meisten AT90, siehe Dokumentation der avr-libc für unterstützte Typen). Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man [[AVR#Starterkits|hier]].&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc Compiler und die avr-libc. Kostenlos erhältlich für nahezu alle Plattformen und Betriebssysteme. Für MS-Windows im Paket [[WinAVR]]; für Unix/Linux siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]]).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder nur die eigenen C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
&lt;br /&gt;
* Die [[AVR Checkliste]] durcharbeiten.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/2 GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  auf den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
&lt;br /&gt;
* Die Beispieleprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweisen sind aber auch auf C-Programme übertragbar.&lt;br /&gt;
&lt;br /&gt;
* Einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien, um das Problem nachzuvollziehen, sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z. B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.&amp;amp;nbsp;B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.&amp;amp;nbsp;B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen, um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu zwei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer integrierten Entwicklungsumgebung (IDE = &#039;&#039;&#039;I&#039;&#039;&#039;ntegrated &#039;&#039;&#039;D&#039;&#039;&#039;evelopment &#039;&#039;&#039;E&#039;&#039;&#039;nvironment), bei der alle Einstellungen z.&amp;amp;nbsp;B. in Dialogboxen durchgeführt werden können. Unter Anderem kann AVRStudio ab Version 4.12 (kostenlos auf [http://www.atmel.com/ atmel.com]) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs für den avr-gcc (ohne Anspruch auf Vollständigkeit): AtmanAvr C (relativ günstig), KamAVR (kostenlos), VMLab (ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.&amp;amp;nbsp;B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand der universellen und plattformunabhängigen Vorgehensweise mittels make und Makefiles näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind lediglich Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xff;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5a)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/;  // (5b)&lt;br /&gt;
   }                         // (5c)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (6)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In der mit (1) markierten Zeile wird eine so genannte Header-Datei eingebunden. In io.h sind die Registernamen definiert, die im späteren Verlauf genutzt werden.&lt;br /&gt;
&lt;br /&gt;
* Bei (2) beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion main.&lt;br /&gt;
&lt;br /&gt;
* Die Anschlüsse eines AVR (&amp;quot;Beinchen&amp;quot;) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Richtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B zu Ausgängen.&lt;br /&gt;
&lt;br /&gt;
* (4) stellt die Werte der Ausgänge ein. Die den ersten beiden Bits des Ports zugeordneten Anschlüsse (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential).&lt;br /&gt;
&lt;br /&gt;
* (5) ist die so genannte Hauptschleife (main-loop). Dies ist eine Programmschleife, welche kontinuierlich wiederkehrende Befehle enthält. In diesem Beispiel ist sie leer. Der Controller durchläuft die Schleife immer wieder, ohne dass etwas passiert (außer das Strom verbraucht wird). Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Ohne diese Schleife wäre der Zustand des Controllers nach dem Programmende undefiniert.&lt;br /&gt;
&lt;br /&gt;
* (6) wäre das Programmende. Die Zeile ist nur aus Gründen der C-Kompatibilität enthalten: int main(void) besagt, dass die Funktion einen Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein auf dem Controller lauffähiges Programm zu übersetzen, wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen Makefile (ohne Endung) im selben Verzeichnis, in dem auch die Datei main.c mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im folgenden Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\tmp\gcc_tut\quickstart&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei main.hex, in der der Code für den AVR enthalten ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.&amp;amp;nbsp;B. über In-System-Programming (ISP) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann (z.&amp;amp;nbsp;B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio), kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine LED leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode mit einem dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Exkurs: Makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebungen à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Plattformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt, genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.com) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das Makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den im Makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im Makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten Makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt [[AVR-GCC-Tutorial#EEPROM|Speicherzugriffe]]), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendeten Controllers gesetzt. Eine Liste der von avr-gcc und der avr-libc unterstützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien eintragen ==&lt;br /&gt;
&lt;br /&gt;
Der Name der Quellcodedatei, welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.&amp;amp;nbsp;B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
# nicht auskommentiert: EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Das erstellte Makefile und der Code müssen im gleichen Ordner sein, auch sollte der Dateiname nicht verändert werden. &lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem Makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschinencode erzeugt. Beim Update auf eine neue Compilerversion ist zu beachten, dass diese möglicherweise intern andere Optimierungsalgorithmen verwendet und sich dadurch die Größe des Machinencodes etwas ändert, ohne dass man im Quellcode etwas geändert hat.&lt;br /&gt;
&lt;br /&gt;
Als Orientierungswerte die Größe des Maschinencodes bei verschiedenen Optionen für einen nicht näher spezifizierten relativ kleinen Testcode bei Verwendung einer nicht näher spezifizierten Compilerversion. &lt;br /&gt;
&lt;br /&gt;
* -O0 : 12&#039;217 Byte&lt;br /&gt;
&lt;br /&gt;
* -O1 : 9&#039;128 Byte&lt;br /&gt;
&lt;br /&gt;
* -O2 : 1&#039;670 Byte&lt;br /&gt;
&lt;br /&gt;
* -O3 : 3&#039;004 Byte&lt;br /&gt;
&lt;br /&gt;
* -Os : 1&#039;695 Byte&lt;br /&gt;
&lt;br /&gt;
Im diesem Testfall führt die Option -O2 mit zum kompaktesten Code, dies  allerdings hier nur mit 25 Bytes &amp;quot;Vorsprung&amp;quot;. Es kann durchaus sein, dass nur wenige Programmerweiterungen dazu führen, dass Compilieren mit -Os wieder in kompakteren Code resultiert.&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/using_tools.html#gcc_optO avr-libc manual Abschnitt Using the gnu-tools/Compiler-Optionen]&lt;br /&gt;
&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_optflags avr-libc Manual FAQ Nr. 16] (Stand avr-libc Version 1.4.5)&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wird hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (großes S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten, lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Taktfrequenz ===&lt;br /&gt;
&lt;br /&gt;
Neuere Versionen der WinAVR/Mfile Vorlage für Makefiles beinhalten die Definition einer Variablen F_CPU (WinAVR 2/2005). Darin wird die Taktfrequenz des Controllers in Hertz eingetragen. Die Definition steht dann im gesamten Projekt ebenfalls unter der Bezeichnung F_CPU zur Verfügung (z.&amp;amp;nbsp;B. um daraus UART-, SPI- oder ADC-Frequenzeinstellungen abzuleiten).&lt;br /&gt;
&lt;br /&gt;
Die Angabe hat rein &amp;quot;informativen&amp;quot; Charakter, die tatsächliche Taktrate wird über den externen Takt (z.&amp;amp;nbsp;B. Quarz) bzw. die Einstellung des internen R/C-Oszillators  bestimmt. Die Nutzung von F_CPU hat also nur Sinn, wenn die Angabe mit dem tatsächlichen Takt übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Innerhalb neuerer Versionen der avr-libc (ab Version 1.2) wird die Definition der Taktfrequenz (F_CPU) zur Berechnung der Wartefunktionen in delay.h genutzt. Diese funktionieren nur dann korrekt, wenn F_CPU mit der tatsächlichen Taktfrequenz übereinstimmt.&lt;br /&gt;
F_CPU muss dazu jedoch nicht unbedingt im makefile definiert werden. Es reicht aus, wird aber bei mehrfacher Anwendung unübersichtlich, vor &#039;&#039;#include &amp;lt;util/delay.h&amp;gt;&#039;&#039; (veraltet: &#039;&#039;#include &amp;lt;avr/delay.h&amp;gt;&#039;&#039;) ein &#039;&#039;#define F_CPU [hier Takt in Hz]UL&#039;&#039; einzufügen. Bei Nutzung von delay.h ist darauf zu achten, dass die Optimierung des Compilers nicht ausgeschaltet ist, sonst wird sehr viel Code erzeugt und die Wartezeit stimmt nicht mit der gewünschten Zeitspanne überein. Vgl. dazu den [http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html entsprechenden Abschnitt der Dokumentation].&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage so genannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* Im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
* Die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* Weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff: (sollte nur noch in Ausnahmefällen genutzt werden)&lt;br /&gt;
&lt;br /&gt;
* Im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
* Die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* Weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler diese &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, einem Nachbau davon (BootICE, Evertool o.&amp;amp;nbsp;ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
Die Verwendung von Makefiles bietet noch viele weitere Möglichkeiten, einige davon werden im Anhang [[AVR-GCC-Tutorial#Zus.C3.A4tzliche_Funktionen_im_Makefile|Zusätzliche Funktionen im Makfile]] erläutert.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige (Integer) Datentypen =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei stdint.h definiert. &lt;br /&gt;
Zur Nutzung der standardisierten Typen bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// ab avr-libc Version 1.2.0 möglich und empfohlen:&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// veraltet: #include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef signed char        int8_t;&lt;br /&gt;
typedef unsigned char      uint8_t;&lt;br /&gt;
&lt;br /&gt;
typedef short              int16_t;&lt;br /&gt;
typedef unsigned short     uint16_t;&lt;br /&gt;
&lt;br /&gt;
typedef long               int32_t;&lt;br /&gt;
typedef unsigned long      uint32_t;&lt;br /&gt;
&lt;br /&gt;
typedef long long          int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* int8_t steht für einen 8-Bit Integer mit einem Wertebereich -128 bis +127.&lt;br /&gt;
* uint8_t steht für einen 8-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 255&lt;br /&gt;
&lt;br /&gt;
* int16_t steht für einen 16-Bit Integer mit einem Wertebereich -32768 bis +32767.&lt;br /&gt;
* uint16_t steht für einen 16-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 65535.&lt;br /&gt;
&lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; werden vorzeichenbehaftete Zahlen abgespeichert. Typen mit vorgestelltem &#039;&#039;u&#039;&#039; dienen der Ablage von postiven Zahlen (inkl. 0).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/(Standard) Integer Types&lt;br /&gt;
&lt;br /&gt;
= Bitfelder =&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&lt;br /&gt;
&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in eine einzelne Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitfelder sparen Platz im RAM, zu Lasten von Platz im Flash, verschlechtern aber unter Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.&lt;br /&gt;
&lt;br /&gt;
Wenn man nur eine paar wenige Variablen vom Typ bool verwenden möchte, kann man&lt;br /&gt;
auch die Headerdatei &amp;lt;stdbool.h&amp;gt; einbinden und sich dann wie gewohnt einen Booltyp anlegen. Variablen dieses Typs brauchen dennoch 1 Byte Speicher, ermöglichen aber eine genaue Unterscheidung zwischen Zahlenvariable und boolscher Variable.&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;C-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum können für allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden&lt;br /&gt;
AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039;, wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output).  Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt.&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend&lt;br /&gt;
angepasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O-Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek (avr-libc) stellt auch Funktionen zur Abfrage eines einzelnen Bits eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist, wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind &#039;&#039;nicht erforderlich&#039;&#039;, man kann auch &amp;quot;einfache&amp;quot; C-Syntax verwenden, die universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) wenn das Bit gesetzt und 0 (&amp;quot;falsch&amp;quot;) wenn es nicht gesetzt ist. Die Negierung des Ausdrucks, also  &#039;&#039;&#039;!&#039;&#039;&#039;(Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)), entspricht &#039;&#039;bit_is_clear&#039;&#039; und gibt einen Wert &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) zurück, falls das Bit nicht gesetzt ist,&lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O-Register einfach wie eine Variable setzen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und outp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben von Bits ====&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausdruck:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit (für 1) eines Bytes hat die Bitnummer 0, das &amp;quot;höchstwertige&amp;quot; (für 128) die Nummer 7.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );    /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3);   /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die warten, bis ein bestimmter Zustand auf einem Bit erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend gewartet&amp;quot; wird. D.h., der Programmablauf bleibt an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den Watchdog ein, muss man darauf achten, dass dieser auch noch getriggert wird (zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Änderung: Habe PINx und PORTx vertauscht.PINx ist für Pull up und PORTx für high bzw low. Michael.L  - &lt;br /&gt;
&lt;br /&gt;
Was sollte diese Aenderung?? Den Rest des Abschnitts auch gelesen? Evtl. die hier nicht erlaeuterten Zusatzfunktion der neuen AVRs durcheinandergeworfen? Aenderung verworfen bis mehr Details.  m.thomas. &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
// Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
&lt;br /&gt;
DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
/* mehr Tipparbeit aber übersichtlicher: */&lt;br /&gt;
DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&lt;br /&gt;
/* Einige Compiler erlauben die Eingabe von Konstanten im &lt;br /&gt;
   Binärformat, z.B. gcc in WinAVR, NICHT aber aus&lt;br /&gt;
   GNU-gcc-Quellen selbst erstellte Compiler. Diese Schreib-&lt;br /&gt;
   weise also nicht nutzen, wenn Code mit anderen &lt;br /&gt;
   ausgetauscht werden soll. */&lt;br /&gt;
DDRB = 0b00011111;    /* direkte Zuweisung &lt;br /&gt;
                         - übersichtlicher &lt;br /&gt;
                         - nicht standardkonform &lt;br /&gt;
                         - selten portabel&lt;br /&gt;
                         - nur bei modifiziertem GCC */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
Weitere Beispiele:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
DDRB = 0xff; &lt;br /&gt;
// Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; DDB0 );&lt;br /&gt;
// Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( ( 1 &amp;lt;&amp;lt; DDB3 ) | ( 1&amp;lt;&amp;lt;DDB4) );&lt;br /&gt;
// Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB |= ( 1 &amp;lt;&amp;lt; DDB0) | ( 1 &amp;lt;&amp;lt; DDB3 );&lt;br /&gt;
// Alle Pins auf Eingang:&lt;br /&gt;
DDRB = 0x00;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&#039;&#039;&lt;br /&gt;
* &#039;&#039;zuerst die Bits im PORTx-Register setzen und&#039;&#039;&lt;br /&gt;
* &#039;&#039;anschließend die Datenrichtung auf Ausgang stellen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Daraus ergibt sich die Abfolge für einen Eingangspin:&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze PORTx: interner Pull-Up aktiv&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze DDRx: Ausgang &amp;quot;high&amp;quot;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bei der Reihenfolge erst DDRx und dann PORTx, kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;&#039;nicht&#039;&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, &#039;&#039;&#039;sondern&#039;&#039;&#039; Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.&amp;lt;/font&amp;gt; (Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände.)&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch.  Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVRs haben sogar an den meisten Pins softwaremäßig zuschaltbare interne Pull-Up Widerstände, welche wir natürlich auch verwenden können.&lt;br /&gt;
&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;DDC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== (Tasten-)Entprellung ====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim Schließen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&lt;br /&gt;
&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen als mehrfache Impulse gezählt wird. Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* bei alter avr-libc: #include &amp;lt;avr/delay.h&amp;gt; */      &lt;br /&gt;
&lt;br /&gt;
/* Einfache Funktion zum Entprellen eines Tasters */&lt;br /&gt;
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)&lt;br /&gt;
{&lt;br /&gt;
    if ( ! (*port &amp;amp; (1 &amp;lt;&amp;lt; pin)) )&lt;br /&gt;
    {&lt;br /&gt;
        /* Pin wurde auf Masse gezogen, 100ms warten   */&lt;br /&gt;
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz&lt;br /&gt;
        _delay_ms(50); &lt;br /&gt;
        if ( *port &amp;amp; (1 &amp;lt;&amp;lt; pin) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Anwender Zeit zum Loslassen des Tasters geben */&lt;br /&gt;
            _delay_ms(50);&lt;br /&gt;
            _delay_ms(50); &lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; PB0 );                 /* PIN PB0 auf Eingang (Taster)            */&lt;br /&gt;
    PORTB |= ( 1 &amp;lt;&amp;lt; PB0 );                 /* Pullup-Widerstand aktivieren            */&lt;br /&gt;
    ...&lt;br /&gt;
    if (debounce(&amp;amp;PINB, PB0))             /* Falls Taster an PIN PB0 gedrueckt..    */&lt;br /&gt;
        PORTD = PIND ^ ( 1 &amp;lt;&amp;lt; PD7 );  /* ..LED an Port PD7 an-&lt;br /&gt;
                                   bzw. ausschalten */&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesem Beispiel ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritische Anwendungen sollte man ein anderes Verfahren nutzen (z.B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).&lt;br /&gt;
&lt;br /&gt;
Zum Thema Entprellen siehe auch:&lt;br /&gt;
* Artikel [[Entprellung]]&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls ben&amp;amp;ouml;tigt, kann eine 16-Bit Variable auch recht einfach manuell in ihre zwei 8-Bit Bestandteile zerlegt werden. Folgendes Beispiel demonstriert dies anhand des pseudo- 16-Bit Registers UBRR.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
typedef union {&lt;br /&gt;
        uint16_t i16;&lt;br /&gt;
        struct {&lt;br /&gt;
                uint8_t i8l;&lt;br /&gt;
                uint8_t i8h;&lt;br /&gt;
        };&lt;br /&gt;
} convert16to8;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
convert16to8 baud;&lt;br /&gt;
baud.i16 = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
UBRRH = baud.i8h;&lt;br /&gt;
UBRRL = baud.i8l;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/*alternativ:*/&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint16_t wFoo16;&lt;br /&gt;
uint8_t bFooLow, bFooHigh;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
wFoo16   = 0xAA55;                 /* zu &amp;quot;zerlegende&amp;quot; 16Bit-Integer */&lt;br /&gt;
bFooHigh = (uint8_t)(wFoo16 &amp;gt;&amp;gt; 8); /* MS-Byte */&lt;br /&gt;
bFooLow  = (uint8_t)(wFoo16);      /* LS-Byte */&lt;br /&gt;
...&lt;br /&gt;
#define us0(Data) ((unsigned char *)(&amp;amp;Data))&lt;br /&gt;
#define us1(Data) ((unsigned char *)((&amp;amp;Data)+1))&lt;br /&gt;
bFooHigh = us1(wFoo16);&lt;br /&gt;
bFoolow  = us0(wFoo16);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen (z.B. ATmega8) teilen sich UBRRH und UCSRC die gleiche Memory-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL) welches Register tats&amp;amp;auml;chlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und &amp;amp;uuml;bergibt den Wert &#039;&#039;3&#039;&#039; und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und &amp;amp;uuml;bergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Bei einigen 16-bit Registern (insbesondere die der 16-bit Timer) erfolgen Schreibzugriffe über das sogenannte &#039;&#039;temporary Register&#039;&#039;. Die Reihenfolge der Zugriffe bestimmt, wann der Wert tatsächlich ins Register geschrieben wird. Typisch wird erst das High-Byte beschrieben (xxxH) und intern im temporary Register zwischengespeichert. Nachdem das Low-Byte (xxxL) geschrieben wurde, setzt der Controller mit diesem und dem im temporary Register zwischengespeicherten Wert für das High-Byte das 16-bit Register. Dabei ist zu beachten, dass intern nur ein temporary Register verfügbar ist, welches in Interruptroutinen mglw. mit einem anderen Wert überschrieben wird, wenn dort ebenfalls 16-bit Register beschrieben werden. avr-gcc/avr-libc berücksichtigen die korrekte Reihenfolge automatisch, wenn die Register mit ihrem &amp;quot;16-bit Label&amp;quot; (ohne H bzw. L) angesprochen werden, dabei ist der Schutz des temporary Registers vor Überschreiben durch Interruptroutinen dennoch zu beachten (im Zweifel beim Schreibzugriff die Interrupts kurzzeitig global deaktivieren).&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t key_pressed(const volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if ( last_state == ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) ) ) {&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    i = key_pressed( &amp;amp;PINB, PB1 );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Im Übrigen können mit volatile gekennzeichnete Variablen auch als const deklariert werden, um sicherzustellen, dass sie nur noch von der Hardware änderbar sind.&lt;br /&gt;
&lt;br /&gt;
= Der UART =&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Über den [[UART]] kann ein AVR leicht mit einer [[RS-232]]-Schnittstelle eines PC oder sonstiger Geräte mit &amp;quot;serieller Schnittstelle&amp;quot; verbunden werden. &lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungen des UART:&lt;br /&gt;
&lt;br /&gt;
* Debug-Schnittstelle: z.B. zur Anzeige von Zwischenergebnissen (&amp;quot;printf-debugging&amp;quot; - hier besser &amp;quot;UART-debugging&amp;quot;) auf einem PC. Auf dem Rechner reicht dazu ein [[RS-232#Terminalprogramme|Terminalprogramm]] (MS-Windows: Hyperterm oder besser Bray-Terminal, [http://www.mikrocontroller.net/forum/read-8-155472.html#new HTerm]; Unix/Linux z.B. minicom). Ein direkter Anschluss ist aufgrund unterschiedlicher Pegel nicht möglich, jedoch sind entsprechende Schnittstellen-ICs wie z.B. ein MAX232 günstig und leicht zu integrieren. Rechner ohne serielle Schnittstelle können über fertige USB-seriell-Adapter angeschlossen werden. &lt;br /&gt;
* &amp;quot;Mensch-Maschine Schnittstelle&amp;quot;: z.B. Konfiguration und Statusabfrage über eine &amp;quot;Kommandozeile&amp;quot; oder Menüs (siehe z.B. Forumsbeitrag [http://www.mikrocontroller.net/topic/52985 Auswertung RS232-Befehle]) &lt;br /&gt;
* Übertragen von gespeicherten Werten: z.B. bei einem Datenlogger&lt;br /&gt;
* Anschluss von Geräten mit serieller Schnittstelle (z.B. (Funk-)Modems, Mobiltelefone, Drucker, Sensoren, &amp;quot;intelligente&amp;quot; LC-Displays, GPS-Empfänger). &lt;br /&gt;
* &amp;quot;Feldbusse&amp;quot; auf RS485/RS422-Basis mittels entsprechenden Bustreiberbausteinen (z.B. MAX485)&lt;br /&gt;
* DMX, Midi etc.&lt;br /&gt;
* LIN-Bus (&#039;&#039;&#039;L&#039;&#039;&#039;ocal &#039;&#039;&#039;I&#039;&#039;&#039;nterconnect &#039;&#039;&#039;N&#039;&#039;&#039;etwork): Preiswerte Sensoren/Aktoren in der Automobiltechnik und darüber hinaus&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben ein bis zwei vollduplexfähigen UART (&#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) schon eingebaut (&amp;quot;Hardware-UART&amp;quot;). &lt;br /&gt;
Übrigens: Vollduplex heißt nichts anderes, als dass der Baustein gleichzeitig senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega, ATtiny) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterscheidet sich vom UART hauptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurationsregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit zeigt an, ob der Sendepuffer bereit ist, um ein zu sendendes Zeichen aufzunehmen. Das Bit wird vom AVR gesetzt (1), wenn der Sendepuffer leer ist. Es wird gelöscht (0), wenn ein Zeichen im Sendedatenregister vorhanden ist und noch nicht in das Sendeschieberegister übernommen wurde. Atmel empfiehlt aus Kompatibilitätsgründen mit kommenden µC, UDRE auf 0 zu setzen, wenn das UCSRA Register beschrieben wird.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
\mathrm{UBRR} = \frac{\mathrm{Taktfrequenz}}{\mathrm{Baudrate} \cdot 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS-232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. &lt;br /&gt;
&amp;lt;!-- &amp;quot;Hackerloesung&amp;quot; auskommentiert - nicht so gut in einem &amp;quot;Einsteiger-Tutorial&amp;quot; - mthomas&lt;br /&gt;
Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfiehlt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== UART initialisieren ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben. Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach gewünschter Funktionsweise die benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Da wir vorerst nur senden möchten und noch keine Interrupts auswerten wollen, gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs mit USART haben mehrere Konfigurationsregister und erfordern eine etwas andere Konfiguration. Für einen ATmega16 z.B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                 // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);     // Asynchron 8N1&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist noch das Baudratenregister &#039;&#039;&#039;UBRR&#039;&#039;&#039; einzustellen. Bei neueren AVRs besteht es aus zwei Registern &#039;&#039;&#039;UBRRL&#039;&#039;&#039; und &#039;&#039;&#039;UBRRH&#039;&#039;&#039;. Der Wert dafür ergibt sich aus der angegebenen Formel durch Einsetzen der Taktfrequenz und der gewünschten Übertragungsrate. Das Berechnen der Formel wird dem [[C-Präprozessor|Präprozessor]] überlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann&lt;br /&gt;
   F_CPU im Makefile definiert werden, eine nochmalige Definition&lt;br /&gt;
   hier wuerde zu einer Compilerwarnung fuehren. Daher &amp;quot;Schutz&amp;quot; durch&lt;br /&gt;
   #ifndef/#endif &lt;br /&gt;
&lt;br /&gt;
   Dieser &amp;quot;Schutz&amp;quot; kann zu Debugsessions führen, wenn AVRStudio &lt;br /&gt;
   verwendet wird und dort eine andere, nicht zur Hardware passende &lt;br /&gt;
   Taktrate eingestellt ist: Dann wird die folgende Definition &lt;br /&gt;
   nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) &lt;br /&gt;
   von AVRStudio - daher Ausgabe einer Warnung falls F_CPU&lt;br /&gt;
   noch nicht definiert: */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000&amp;quot;&lt;br /&gt;
#define F_CPU 4000000UL    // Systemtakt in Hz &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600UL          // Baudrate&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) // Fehler in Promille, 1000 = kein Fehler.&lt;br /&gt;
&lt;br /&gt;
#if ((BAUD_ERROR&amp;lt;990) || (BAUD_ERROR&amp;gt;1010))&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;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    UBRR = UBRR_VAL;&lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&amp;lt;!-- mthomas: warum nicht UL?, wird von AVRStudio auch mit UL übergeben --&amp;gt;&lt;br /&gt;
Wieder für den Mega16 mit zwei Registern für die Baudrateneinstellung eine etwas andere Programmierung. Wichtig ist, dass UBRRH &#039;&#039;&#039;vor&#039;&#039;&#039; UBRRL geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmega16 */&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einige AVR (z.B. ATmega169, ATmega48/88/168, AT90CAN jedoch nicht für z.B. ATmega16/32, ATmega128, ATtiny2313) wird durch die Registerdefinitionen der avr-libc (io*.h) auch für Controller mit zwei UBRR-Registern (UBRRL/UBRRH) ein UBRR bzw. UBRR0 als &amp;quot;16-bit-Register&amp;quot; definiert und man kann auch Werte direkt per UBRR = UBRR_VAL zuweisen. Intern werden dann zwei Zuweisungen für UBRRH und UBRRL generiert. Dies ist nicht bei allen Controllern möglich, da die beiden Register nicht bei allen aufeinanderfolgende Addressen aufweisen. Die getrennte Zuweisung an UBRRH und UBRRL wie im Beispiel gezeigt ist universeller und portabler und daher vorzuziehen.&lt;br /&gt;
&lt;br /&gt;
Die Makros sind sehr praktisch, da sie sowohl automatisch den Wert für UBRR als auch den Fehler in der generierten Baudrate berechnen und im Falle einer Überschreitung (+/-1%) einen Fehler und somit Abbruch im Compilerablauf generieren. Damit können viele Probleme mit &amp;quot;UART sendet komische Zeichen&amp;quot; vermieden werden. Ausserdem kann man mühelos die Einstellung an eine neue Taktfrequenz bzw. Baudrate anpassen, ohne selber rechnen oder in Tabellen nachschlagen zu müssen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. Vorher ist zu prüfen, ob das UART-Modul bereit ist, das zu sendende Zeichen entgegenzunehmen. Die Bezeichnungen des/der Statusregisters mit dem Bit UDRE ist abhängig vom Controllertypen (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    // bei AVR mit einem UART (&amp;quot;classic AVR&amp;quot; z.B. AT90S8515)&lt;br /&gt;
    while (!(USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                  /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&lt;br /&gt;
    /** ODER **/&lt;br /&gt;
&lt;br /&gt;
    // bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                    /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe &amp;quot;String senden&amp;quot; wird durch zwei Funktionen abgearbeitet. Die universelle/controllerunabhängige Funktion uart_puts übergibt jeweils ein Zeichen der Zeichenkette an eine Funktion uart_putc, die abhängig von der vorhandenen Hardware implementiert werden muss. In der Funktion zum Senden eines Zeichens ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// putc fuer AVR mit einem UART (z.B. AT90S8515)&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while(!(USR &amp;amp; (1 &amp;lt;&amp;lt; UDRE)))  /* warte, bis UDR bereit */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = c;                     /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** ODER **/&lt;br /&gt;
&lt;br /&gt;
// bei neueren AVRs andere Bezeichnung fuer die Statusregister, hier ATmega16:&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich */&lt;br /&gt;
    {&lt;br /&gt;
    }                             &lt;br /&gt;
&lt;br /&gt;
    UDR = c;                      /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* puts ist unabhaengig vom Controllertyp */&lt;br /&gt;
void uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != &#039;\0&#039; also ungleich dem &amp;quot;String-Endezeichen&amp;quot; */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die in uart_putc verwendeten Schleifen, in denen gewartet wird bis die UART-Hardware zum senden bereit ist, sind insofern etwas kritisch, da während des Sendens eines Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. empfangenen Zeichen/Bytes zwischengespeichert und in Interruptroutinen an die U(S)ART-Hardware weitergegeben bzw. von ihr ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&lt;br /&gt;
&lt;br /&gt;
=== Schreiben von Variableninhalten ===&lt;br /&gt;
&lt;br /&gt;
Sollen Inhalte von Variablen (Ganzzahlen, Fließkomma) in &amp;quot;menschenlesbarer&amp;quot; Form gesendet werden, ist vor dem Transfer eine Umwandlung in Zeichen (&amp;quot;ASCII&amp;quot;) erforderlich. Bei nur einer Ziffer ist diese Umwandlung relativ einfach: man addiert den ASCII-Wert von Null zur Ziffer und kann diesen Wert direkt senden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_putc (s.o.)&lt;br /&gt;
&lt;br /&gt;
// Ausgabe von 0123456789&lt;br /&gt;
char c;&lt;br /&gt;
&lt;br /&gt;
for (uint8_t i=0; i&amp;lt;=9; ++i) {&lt;br /&gt;
    c = i + &#039;0&#039;;&lt;br /&gt;
    uart_putc( c );&lt;br /&gt;
    // verkuerzt: uart_putc( i + &#039;0&#039; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll mehr als eine Ziffer ausgegeben werden, bedient man sich zweckmäßigerweise vorhandener Funktionen zur Umwandlung von Zahlen in Zeichenketten/Strings. Die Funktion der avr-libc zur Umwandlung von vorzeichenbehafteten 16bit-Ganzzahlen (int16_t) in Zeichenketten heißt &#039;&#039;itoa&#039;&#039; (Integer to ASCII). Man muss der Funktion einen Speicherbereich zur Verarbeitung (buffer) mit Platz für alle Ziffern, das String-Endezeichen (&#039;\0&#039;) und evtl. das Vorzeichen bereitstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   char s[7];&lt;br /&gt;
   int16_t i = -12345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   itoa( i, s, 10 ); // 10 fuer radix -&amp;gt; Dezimalsystem&lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // da itoa einen Zeiger auf den Beginn von s zurueckgibt verkuerzt auch:&lt;br /&gt;
   uart_puts( itoa( i, s, 10 ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für vorzeichenlose 16bit-Ganzzahlen (uint16_t) exisitert &#039;&#039;utoa&#039;&#039;. Die Funktionen für 32bit-Ganzzahlen (int32_t und uint32_t) heißen &#039;&#039;ltoa&#039;&#039; bzw. &#039;&#039;ultoa&#039;&#039;. Da 32bit-Ganzzahlen mehr Stellen aufweisen können, ist ein entsprechend größerer Pufferspeicher vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Auch Fließkommazahlen (float/double) können mit breits vorhandenen Funktionen in Zeichenfolgen umgewandelt werden, dazu existieren die Funktionen &#039;&#039;dtostre&#039;&#039; und &#039;&#039;dtostrf&#039;&#039;. dtostre nutzt Exponentialschreibweise (&amp;quot;engineering&amp;quot;-Format). (Hinweis: z.Zt. existiert im avr-gcc kein &amp;quot;echtes&amp;quot; double, intern wird immer mit &amp;quot;einfacher Genauigkeit&amp;quot;, entsprechend float, gerechnet.) &lt;br /&gt;
&lt;br /&gt;
dtostrf und dtostre benötigen die libm.a der avr-libc. Bei Nutzung von Makefiles ist der Parameter -lm in in LDFLAGS anzugeben (Standard in den WinAVR/mfile-Makefilevorlagen). Nutzt man AVRStudio als IDE für den GNU-Compiler (gcc-Plugin) ist die libm.a unter Libaries auszuwählen: Project -&amp;gt; Configurations Options -&amp;gt; Libaries -&amp;gt; libm.a mit dem Pfeil nach rechts einbinden. Siehe auch die [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|FAQ]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
/* lt. avr-libc Dokumentation:&lt;br /&gt;
char* dtostrf(&lt;br /&gt;
  double __val,&lt;br /&gt;
  char   __width,&lt;br /&gt;
  char   __prec,&lt;br /&gt;
  char * __s&lt;br /&gt;
)  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   // Pufferspeicher ausreichend groß&lt;br /&gt;
   // evtl. Vorzeichen + width + Endezeichen:&lt;br /&gt;
   char s[8]; &lt;br /&gt;
   float f = -12.345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   dtostrf( f, 6, 3, s ); &lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // verkürzt: uart_puts( dtostrf( f, 7, 3, s ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfangen ==&lt;br /&gt;
=== einzelne Zeichen empfangen ===&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von Zeichen muss der Empfangsteil des UART bei der Initialisierung aktiviert werden, indem das RXEN-Bit im jeweiligen Konfigurationsregister (UCSRB bzw UCSR0B/UCSR1B) gesetzt wird. Im einfachsten Fall wird solange gewartet, bis ein Zeichen empfangen wurde, dieses steht dann im UART-Datenregister (UDR bzw. UDR0 und UDR1 bei AVRs mit 2 UARTS) zur Verfügung (sogen. &amp;quot;Polling-Betrieb&amp;quot;). Ein Beispiel für den ATmega16:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Zusaetzlich zur Baudrateneinstellung und der weiteren Initialisierung: */&lt;br /&gt;
void Usart_EnableRX()&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= ( 1 &amp;lt;&amp;lt; RXEN );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion blockiert den Programmablauf. Alternativ kann das RXC-Bit in einer Programmschleife abgefragt werden und dann nur bei gesetztem RXC-Bit UDR ausgelesen werden. Eleganter und in den meisten Anwendungsfällen &amp;quot;stabiler&amp;quot; ist die Vorgehensweise, die empfangenen Zeichen in einer Interrupt-Routine einzulesen und zur späteren Verarbeitung in einem Eingangsbuffer (FIFO-Buffer) zwischenzuspeichern. Dazu existieren fertige und gut getestete [[Libraries|Bibliotheken]] &amp;lt;!-- &amp;quot;echte Libraries&amp;quot; (.a) wie im Verweis beschrieben sind hier eigentlich nicht gemeint, verwirrt hier etwas, da AVR-&amp;quot;Libraries&amp;quot; meist per #defines anpassbare Source-Codes sind, vielleicht so: --&amp;gt; und Quellcodekomponenten (z.B. UART-Library von P. Fleury, procyon-avrlib und einige in der &amp;quot;Academy&amp;quot; von avrfreaks.net).&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html Dokumenation der avr-libc/stdlib.h]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_printf Die Nutzung von printf]&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&amp;lt;!-- nimmermehr: * siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (printf etc.) im [[AVR-Tutorial:_UART]]. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: 9bit&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Zeichenketten (Strings) ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Zeichenketten, muß man sich zunächst darüber im klaren sein, daß es ein Kriterium geben muß, an dem der µC erkennen kann, wann ein String zu Ende ist. Sehr oft wird dazu das Zeichen &#039;Return&#039; benutzt, um das Ende eines Strings zu markieren. Dies ist vom Benutzer einfach eingebbar und er ist auch daran gewöhnt, daß er eine Eingabezeile mit einem Druck auf die Return Taste abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Prinzipiell gibt es jedoch keine Einschränkung bezüglich dieses speziellen Zeichens. Es muß nur sichergestellt werden, daß dieses spezielle &#039;Ende eines Strings&#039; - Zeichen nicht mit einem im Text vorkommenden Zeichen verwechselt werden kann. Wenn also im zu übertragenden Text beispielsweise kein &#039;;&#039; vorkommt, dann spricht nichts dagegen, einen String mit einem &#039;;&#039; abschließen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird die durchaus übliche Annahme getroffen, daß eine Stringübertragung identisch ist mit der Übertragung einer Textzeile und daher mit einem Return (&#039;\n&#039;) abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Das Problem der Übertragung eines Strings reduziert sich damit auf die Aufgabenstellung: Empfange und Sammle Zeichen in einem char Array, bis entweder das Array voll ist oder das &#039;String Ende Zeichen&#039; empfangen wurde. Danach wird der empfangene Text noch mit einem &#039;\0&#039; Zeichen abgeschlossen um einen Standard C-String daraus zu machen, mit dem dann weiter gearbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_gets( char* Buffer, uint8_t MaxLen )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t NextChar;&lt;br /&gt;
  uint8_t StringLen = 0;&lt;br /&gt;
&lt;br /&gt;
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen&lt;br /&gt;
&lt;br /&gt;
                                  // Sammle solange Zeichen, bis:&lt;br /&gt;
                                  // * entweder das String Ende Zeichen kam&lt;br /&gt;
                                  // * oder das aufnehmende Array voll ist&lt;br /&gt;
  while( NextChar != &#039;\n&#039; &amp;amp;&amp;amp; StringLen &amp;lt; MaxLen - 1 ) {&lt;br /&gt;
    *Buffer++ = NextChar;&lt;br /&gt;
    StringLen++;&lt;br /&gt;
    NextChar = uart_getc();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
                                  // Noch ein &#039;\0&#039; anhängen um einen Standard&lt;br /&gt;
                                  // C-String daraus zu machen&lt;br /&gt;
  *Buffer = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf ist darauf zu achten, dass das empfangende Array auch mit einer&lt;br /&gt;
vernünftigen Größe definiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
  char Line[40];      // String mit maximal 39 zeichen&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) );&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Interruptbetrieb==&lt;br /&gt;
&lt;br /&gt;
Beim ATMEGA8 ist es möglich, dass RXCIE Bit im Register UCSRB zu setzen.&lt;br /&gt;
Dieser Interrupt wird immer ausglöst, wenn Daten erfolgreich empfangen wurden.&lt;br /&gt;
Zusätzlich braucht man die Routine:&lt;br /&gt;
&lt;br /&gt;
ISR (USART_RXC_vect) {&lt;br /&gt;
   //irgendein Code&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
natürlich muss &amp;quot;Global Interrupt Enable&amp;quot; Aktiviert sein.&lt;br /&gt;
!! Nur getestet beim ATMEGA8 !!&lt;br /&gt;
&lt;br /&gt;
[BAUSTELLE! Aus Lerngründen eventuell als eigenen UART-Interrupt-Block hinter den grundlegenden Interrupt-Teil im Tutorial und hier eine kurze Einführung und einen Verweis darauf anbieten.]&lt;br /&gt;
&lt;br /&gt;
* Unterschied Polling-Betrieb (bisher, oben) und Interrupt-Betrieb&lt;br /&gt;
* Empfangen (Receive)&lt;br /&gt;
** Verändertes UART-Init, ISR (RXC), ggf. Fallstricke ([http://www.mikrocontroller.net/topic/84256#707214 UDR in der ISR lesen!]), Philosophie einer ISR (kurz und schmerzlos), Datenaustausch ISR zu Restprogramm (volatile)&lt;br /&gt;
** Einfachstbeispiel ([http://www.mikrocontroller.net/topic/84228#707052 Echo] (noch buggy beim Datenzugriff, siehe Lit. 2+3!)), ggf. LED zur ISR-Empfangsanzeige oder Overflow-Anzeige&lt;br /&gt;
* FIFO-Puffer, Ringpuffer&lt;br /&gt;
* Senden (Transmit)&lt;br /&gt;
** Variante &amp;quot;UART Data Register Empty&amp;quot; (UDRE) &lt;br /&gt;
** Variante &amp;quot;UART Transmit Complete&amp;quot; (TXC) &lt;br /&gt;
* Fertige UART-Bibliotheken (-&amp;gt;Fleury, -&amp;gt;Procyon)&lt;br /&gt;
* Literatur: &lt;br /&gt;
** 1/ [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=48188 avrfreaks.net Tutorial] inkl. Diskussion (engl.)&lt;br /&gt;
** 2/ avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_16bitio Why do some 16-bit timer registers sometimes get trashed?]&lt;br /&gt;
** 3/ [[Interrupt]] und atomarer Datenzugriff&lt;br /&gt;
&lt;br /&gt;
==Software-UART==&lt;br /&gt;
&lt;br /&gt;
Falls die Zahl der vorhandenen Hardware-UARTs nicht ausreicht, können weitere Schnittstellen über sogennante Software-UARTs ergänzt werden. Es gibt dazu (mindestens) zwei Ansätze: &lt;br /&gt;
* Der bei AVRs üblichste Ansatz basiert auf dem Prinzip, dass ein externer Interrupt-Pin für den Empfang (&amp;quot;RX&amp;quot;) genutzt wird. Das Startbit löst den Interrupt aus, in der Interrupt-Routine (ISR) wird der externe Interrupt deaktiviert und ein Timer aktiviert. In der Interrupt-Routine des Timers wird der Zustand des Empfangs-Pins entsprechend der Baudrate abgetastet. Nach Empfang des Stop-Bits wird der externe Interrupt wieder aktiviert. Senden kann über einen beliebigen Pin (&amp;quot;TX&amp;quot;) erfolgen, der entsprechend der Baudrate und dem zu sendenden Zeichen auf 0 oder 1 gesetzt wird. Die Implementierung ist nicht ganz einfach, es existieren dazu aber fertige Bibliotheken (z.B. bei [http://www.avrfreaks.net/ avrfreaks] oder in der [http://hubbard.engr.scu.edu/embedded/avr/avrlib/ Procyon avrlib]).&lt;br /&gt;
* Ein weiterer Ansatz erfordert keinen Pin mit &amp;quot;Interrupt-Funktion&amp;quot; aber benötigt mehr Rechenzeit. Jeder Input-Pin kann als Empfangspin (RX) dienen. Über einen Timer wird der Zustand des RX-Pins mit einem vielfachen der Baudrate abgetastet (dreifach scheint üblich) und High- bzw. Lowbits anhand einer Mindestanzahl identifiziert. (Beispiel: &amp;quot;Generic Software Uart&amp;quot; Application-Note von IAR)&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (z.B. ATtiny26 oder ATmega48,88,168,169) verfügen über ein Universal Serial Interface (USI), das teilweise UART-Funktion übernehmen kann. Atmel stellt eine Application-Note bereit, in der die Nutzung des USI als UART erläutert wird (im Prinzip &amp;quot;Hardware-unterstützter Software-UART&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==Handshaking==&lt;br /&gt;
Wenn der Sender ständig sendet, wird irgendwann der Fall eintreten, daß der Empfänger nicht bereit ist, neue Zeichen zu empfangen. In diesem Fall muß durch ein &#039;&#039;&#039;Handshake-Verfahren&#039;&#039;&#039; die Situation bereinigt werden. Handshake bedeutet nichts anderes, als daß der Empfänger dem Sender mitteilt, daß er zur Zeit keine Daten annehmen kann und der Sender die Übertragung der nächsten Zeichen solange einstellen soll, bis der Empfänger signalisiert, daß er wieder Zeichen aufnehmen kann.&lt;br /&gt;
===Hardwarehandshake (RTS/CTS)===&lt;br /&gt;
Beim Hardwarehandshake werden zusätzlich zu den beiden Daten-Übertragungsleitungen noch 2 weitere Leitungen benötigt: &#039;&#039;&#039;RTS&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;equest &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end) und &#039;&#039;&#039;CTS&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end). Jeder der beiden Kommunikationspartner ist verpflichtet, bevor ein Zeichen gesendet wird, den Zustand der &#039;&#039;&#039;RTS&#039;&#039;&#039; Leitung zu überprüfen. Nur wenn die Gegenstelle darauf Empfangsbereitschaft signalisiert, darf das Zeichen gesendet werden. Um der Gegenstelle zu signalisieren, daß sie zur Zeit keine Zeichen schicken soll, wird die Leitung &#039;&#039;&#039;CTS&#039;&#039;&#039; benutzt.&lt;br /&gt;
&lt;br /&gt;
===Softwarehandshake (XON/XOFF)===&lt;br /&gt;
Beim Softwarehandshake sind keine speziellen Leitungen notwendig. Statt dessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, daß Senden einzustellen bzw. wieder aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;XOFF&#039;&#039;&#039; Aufforderung das Senden einzustellen&lt;br /&gt;
* &#039;&#039;&#039;XON&#039;&#039;&#039;  Gegenstelle darf wieder senden&lt;br /&gt;
&lt;br /&gt;
Nachteilig bei einem Softwarehandshake ist es, daß dadurch keine direkte binäre Datenübertragung mehr möglich ist. Von den möglichen 256 Bytewerten werden ja 2 (nämlich &#039;&#039;&#039;XON&#039;&#039;&#039; und &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus.&lt;br /&gt;
&lt;br /&gt;
==Fehlersuche==&lt;br /&gt;
Erstaunlich oft wird im Forum der Hilferuf laut: &amp;quot;Meine UART funktioniert nicht, was mache ich falsch&amp;quot;. In der überwiegenden Mehrzahl der Fälle stellt sich dann heraus, daß es sich um ein Hardwareproblem handelt, wobei da wiederrum der Löwenanteil auf das Konto einer nicht korrekt eingestellten Taktrate geht: Der µC benutzt nicht einen angeschlossenen Quarz, so wie er auch im Programm eingetragen ist, sondern läuft immer noch mit dem internen RC-Takt. Daraus resultiert aber auch, daß der Baudraten Konfigurationswert falsch berechnet wird.&lt;br /&gt;
&lt;br /&gt;
Eine Checkliste zum Aufspüren solcher Fehler findet sich [[AVR_Checkliste#UART.2FUSART|hier]].&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Analoge Eingangswerte werden in der Regel über den AVR Analog-Digital-Converter (AD-Wandler, ADC) eingelesen, der in vielen Typen verfügbar ist (typisch 10bit Auflösung). Durch diesen werden analogen Signale (Spannungen) in digitale Zahlenwerte gewandelt. Bei AVRs, die über keinen internen AD-Wandler verfügen (z.B. ATmega162, ATtiny2313), kann durch externe Beschaltung (R/C-Netzwerk und &amp;quot;Zeitmessung&amp;quot;) die Funktion des AD-Wandlers &amp;quot;emuliert&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
Es existieren keine AVRs mit eingebautem Digital-Analog-Konverter (DAC). Diese Funktion muss durch externe Komponenten nachgebildet werden (z.B. PWM und &amp;quot;Glättung&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Unabhängig davon besteht natürlich immer die Möglichkeit, spezielle Bausteine zur Analog-Digital- bzw. Digital-Analog-Wandlung zu nutzen und diese über eine digitale Schnittstelle (z.b. SPI oder I2C) mit einem AVR anzusteuern.&lt;br /&gt;
&lt;br /&gt;
== AC (Analog Comparator) ==&lt;br /&gt;
&lt;br /&gt;
Der Comparator vergleicht 2 Spannungen an den Pins AIN0 und AIN1 und gibt einen Status aus welche der beiden Spannungen größer ist. AIN0 Dient dabei als Referenzspannung (Sollwert) und AIN1 als Vergleichsspannung (Istwert). Als Referenzspannung kann auch alternativ eine interne Referenzspannung ausgewählt werden.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Liegt die Vergleichsspannung (IST) unter der der Referenzspannung (SOLL) gibt der Comperator eine logische 0 aus. ISt die Vergleichsspannung hingegen größer als die Referenzspannung wird eine logische 1 ausgegeben.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Der Comperator arbeitet völlig autark bzw. parallel zum Prozessor. Für mobile Anwendungen empfiehlt es sich ihn abzuschalten sofern er nicht benötigt wird, da er ansonsten Strom benötigt. Der Comparator kann Interruptgesteuert abgefragt werden oder im Pollingbetrieb.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Steuer- bzw. Statusregister ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ACSR (0x28) - Analog Comparator Status Register&amp;lt;BR&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACD&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;AINBG&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACO&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACI&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | n/a&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 7 ACD - Analog Comperator Disable: 0 = Comparator ein, 1 = Comparator aus. Wird dieses Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 ACIE ggf. abgeschaltet werden.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 6 AINBG - Analog Comparator Bandgap Select: Ermöglicht das umschalten zwischen interner und externer Referenzspannung. 1 = interne (~1,3 Volt), 0 = externe Referenzspannung (an Pin AIN0)&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 5 ACO - Analog Coparator Output: Hier wird das Ergebnis des Vergleichs angezeigt. Es liegt typischerweise nach 1-2 Taktzyklen vor. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ergebnis: &amp;lt;BR&amp;gt;IST &amp;lt; SOLL --&amp;gt; 0&amp;lt;BR&amp;gt;&lt;br /&gt;
IST &amp;gt; SOLL --&amp;gt; 1&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4 ACI - Analog Comparator Interrupt Flag: Dieses Bit wird von der Hardware gesetzt wenn ein Interruptereignis das in Bit 0 und 1 definiert ist eintritt. Dieses Bit löst noch keinen Interrupt aus! Die Interruptroutine wird nur dann ausgeführt wenn das Bit 3 ACIE gesetzt ist und global Interrupts erlaubt sind (I-Bit in SREG=1). Das Bit 4 ACI wird wieder gelöscht wenn die Interruptroutine ausgeführt wurde oder wenn manuell das Bit auf 1 gesetzt wird. Das Bit kann für Abfragen genutzt werden, steuert oder konfuguriert aber nicht den Comparator.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 3 ACIE - Analog Comparator Interrupt Enable: Ist das Bit auf 1 gesetzt wird immer ein Interrupt ausgelöst wenn das Ereignis das in Bit 1 und 0 definiert ist eintritt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 2 ACIC - Analog Comparator Input Capture Enable: Wird das Bit gesetzt wird der Comparatorausgang intern mit dem Counter 1 verbunden. Es könnten damit z.b. die Anzahl der Vergleiche im Counter1 gezählt werden. Um den Comparator an den Timer1 Input Capture Interrupt zu verbinden muß im Timerregister das TICIE1 Bit auf 1 gesetzt werden. Der Trigger wird immer dann ausgelöst wenn das in Bit 1 und 0 definierte Ereignis eintritt.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 1,0 ACIS1,ACIS0 - Analog Comparator Interrupt select: Hier wird definiert welche Ereignisse einen Interrupt auslösen sollen:&amp;lt;BR&amp;gt;&lt;br /&gt;
00 = Interrupt auslösen bei jedem Flankenwechsel&amp;lt;BR&amp;gt;&lt;br /&gt;
10 = Interrupt auslösen bei fallender Flanke&amp;lt;BR&amp;gt;&lt;br /&gt;
11 = Interrupt auslösen bei steigender Flanke&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Werden diese Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 gelöscht werden.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der Analog-Digital-Konverter (ADC) wandelt analoge Signale in digitale Werte um, welche vom Controller interpretiert werden können. Einige AVR-Typen haben bereits einen mehrkanaligen Analog-Digital-Konverter eingebaut. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst werden kann, wird durch die Auflösung des ADC in Anzahl Bits angegeben, man hört bzw. liest jeweils von 8-Bit-ADC oder 10-Bit-ADC oder noch höher. ADCs die in AVRs enthalten sind haben zur Zeit eine maximale Auflösung von 10-Bit.&lt;br /&gt;
&lt;br /&gt;
Ein ADC mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des Analog-Digital-Konverters ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Der interne ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem Analog-Digital-Wandler (ADC) zurückgreifen, die über mehrere Kanäle verfügen. Kanäle heißt in diesem Zusammenhang, dass zwar bis zu zehn analoge Eingänge am AVR verfügbar sind, aber nur ein &amp;quot;echter&amp;quot; Analog-Digital-Wandler zur Verfügung steht, vor der eigentlichen Messung ist also einzustellen, welcher Kanal (&amp;quot;Pin&amp;quot;) mit dem Wandler verbunden und gemessen wird.&lt;br /&gt;
&lt;br /&gt;
Die Umwandlung innerhalb des AVR basiert auf der schrittweisen Näherung. Beim AVR müssen die Pins &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AVCC&#039;&#039;&#039; beschaltet werden. Für genaue Messungen sollte AVCC über ein L-C Netzwerk mit VCC verbunden werden, um Spannungsspitzen und -einbrüche vom Analog-Digital-Wandler fernzuhalten. Im Datenblatt findet sich dazu eine Schaltung, die 10uH und 100nF vorsieht.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Analog-Digital-Wandlung wird auf eine Referenzspannung bezogen. Aktuelle AVRs bieten drei Möglichkeiten zur Wahl dieser Spannung:&lt;br /&gt;
&lt;br /&gt;
* Eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; am Anschlusspin &#039;&#039;&#039;AREF&#039;&#039;&#039;. Die minimale (externe) Referenzspannung darf jedoch nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. &lt;br /&gt;
&lt;br /&gt;
* Verfügt der AVR über eine interne Referenzspannung, kann diese genutzt werden. Alle aktuellen AVRs mit internem AD-Wandler sollten damit ausgestattet sein (vgl. Datenblatt: 2,56V oder 1,1V je nach Typ). Das Datenblatt gibt auch über die Genauigkeit dieser Spannung Auskunft.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;interne&#039;&#039;&#039; Referenzspannung wird auf &#039;&#039;&#039;Vcc&#039;&#039;&#039; bezogen, eine externe Referenzspannung auf &#039;&#039;&#039;GND (Masse)&#039;&#039;&#039;. Davon unabhängig werden digitalisierte Spannungen &#039;&#039;&#039;immer&#039;&#039;&#039; auf GND bezogen. Beim ATMEGA8 z.B. ist der Pin AREF über 32kOhm mit GND verbunden, d.h. man muss diese doch extrem niedrige Eingangsimpedanz mit in die Berechnung für einen Spannungsteiler einbeziehen, bzw. kann diesen Widerstand als R2 gleich mit benutzen. Formel für Spannungsteiler: Udiv = U / ((R1 + R2) / R2)&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung von Vcc oder der internen Referenz wird empfohlen, einen Kondensator zwischen dem AREF-Pin und GND anzuordnen. Die Festlegung, welche Spannungsreferenz genutzt wird, erfolgt z.B. beim ATmega16 mit den Bits REFS1/REFS0 im ADMUX-Register. Die zu messende Spannung muss im Bereich zwischen &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AREF&#039;&#039;&#039; (egal ob intern oder extern) liegen. &lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; kann in zwei verschiedenen Betriebsarten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
; Einfache Wandlung (Single Conversion) : In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestoßen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
; Frei laufend (Free Running) : In dieser Betriebsart erfasst der Wandler permanent die anliegende Spannung und schreibt diese in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Die Register des ADC ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; verfügt über eigene Register. Im Folgenden die Registerbeschreibung eines  ATMega16, welcher über 8 ADC-Kanäle verfügt. Die Register unterscheiden sich jedoch nicht erheblich von denen anderer AVRs (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister A.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den &#039;&#039;&#039;ADC&#039;&#039;&#039; verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADEN&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able)&lt;br /&gt;
:Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren. Wenn das Bit nicht gesetzt ist, können die Pins wie normale I/O-Pins verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADSC&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion)&lt;br /&gt;
:Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.&lt;br /&gt;
:Wenn das Bit nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.&lt;br /&gt;
:Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADFR&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;F&#039;&#039;&#039;ree &#039;&#039;&#039;R&#039;&#039;&#039;un select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Ist das Bit auf 1 gesetzt arbeitet der ADC im Freerunning Modus. Dabe iwird das Datenregister permanent aktualisiert. Ist das Bit hingegen auf 0 gesetzt macht der ADC nur eine Single Conversion. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIF&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag)&lt;br /&gt;
:Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt, sobald eine Umwandlung erfolgt ist und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert wurde. Das Bit wird bei lesendem Zugriff auf &#039;&#039;&#039;ADC(L,H)&#039;&#039;&#039; automatisch (d.h. durch die Hardware) gelöscht.&lt;br /&gt;
:Wenn das &#039;&#039;&#039;ADIE&#039;&#039;&#039; Bit sowie das &#039;&#039;&#039;I-Bit&#039;&#039;&#039; im AVR &#039;&#039;&#039;Statusregister&#039;&#039;&#039; gesetzt ist, wird der &#039;&#039;&#039;ADC Interrupt&#039;&#039;&#039; ausgelöst und die Interrupt-Behandlungsroutine aufgerufen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn die Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 in das Register geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIE&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;, dann wird der &#039;&#039;&#039; ADC-Interrupt&#039;&#039;&#039; aktiviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADPS2...ADPS0&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;P&#039;&#039;&#039;rescaler &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
:Der &#039;&#039;&#039;ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz ergibt.&lt;br /&gt;
:Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
TF_{min}=\frac{CLK}{200\,\mathrm{kHz}}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50\,\mathrm{kHz}}=\frac{4000000}{50000}=\mathbf{80}&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im Interesse der schnelleren Wandlungszeit werden wir hier den Faktor 32 einstellen.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCL&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ADCH&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC &#039;&#039;&#039; Data Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn eine Umwandlung abgeschlossen ist, befindet sich der gemessene Wert in&lt;br /&gt;
diesen beiden Registern. Von &#039;&#039;&#039;ADCH&#039;&#039;&#039; werden nur die beiden niederwertigsten Bits verwendet. Es müssen immer beide Register ausgelesen werden, und zwar immer &#039;&#039;&#039;in der Reihenfolge: ADCL, ADCH&#039;&#039;&#039;. &lt;br /&gt;
Der effektive Messwert ergibt sich dann zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCL;       // mit uint16_t x&lt;br /&gt;
x += (ADCH&amp;lt;&amp;lt;8); // in zwei Zeilen (LSB/MSB-Reihenfolge und&lt;br /&gt;
                // C-Operatorpriorität sichergestellt)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADMUX&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Mu&#039;&#039;&#039;ltiple&#039;&#039;&#039;x&#039;&#039;&#039;er Select Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Register wird der zu messende Kanal ausgewählt. Beim 90S8535&lt;br /&gt;
kann jeder Pin von Port A als &#039;&#039;&#039;ADC&#039;&#039;&#039;-Eingang verwendet werden (=8 Kanäle).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADLAR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX4&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX3&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;REFS1...REFS0&#039;&#039;&#039; (&#039;&#039;&#039;Ref&#039;&#039;&#039;erence&#039;&#039;&#039;S&#039;&#039;&#039;election Bits)&lt;br /&gt;
:Mit diesen Bits kann die Referenzspannung eingestellt werden:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Referenzspanung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Externes AREF&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | AVCC als Referenz&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Interne 2,56 Volt&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADLAR&#039;&#039;&#039; (&#039;&#039;&#039;ADC &#039;&#039;&#039; &#039;&#039;&#039;L&#039;&#039;&#039;eft &#039;&#039;&#039;A&#039;&#039;&#039;djust &#039;&#039;&#039;R&#039;&#039;&#039;esult)&lt;br /&gt;
:Das ADLAR Bit verändert das Aussehen des Ergebnisses der AD-Wandlung. Bei einer logischen 1 wird das Ergebnis linksbündig ausgegeben, bei einer 0 rechtsündig. Eine Änderung in diesem Bit beeinflusst das Ergebnis sofort, ganz egal ob bereits eine Wandlung läuft.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX4...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 5 Bits wird der zu messende Kanal bestimmt. Wenn man einen einfachen 1-kanaligen ADC verwendet wird einfach die entsprechende Pinnummer des Ports in die Bits 0...2 eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
:Eine Empfehlung ist deswegen diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren des ADC ====&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039; ADC&#039;&#039;&#039; zu aktivieren, müssen wir das &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bit im &#039;&#039;&#039;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
Ein kleines Beispiel für den &amp;quot;single conversion&amp;quot;-Mode bei einem ATmega169 und Nutzung der internen Referenzspannung (beim &#039;169 1,1V bei anderen AVRs auch 2,56V). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler einstellen. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1023 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint16_t ReadChannel(uint8_t mux)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
&lt;br /&gt;
  ADMUX = mux;                      // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler &lt;br /&gt;
                               // setzen auf 8 (1) und ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  /* nach Aktivieren des ADC wird ein &amp;quot;Dummy-Readout&amp;quot; empfohlen, man liest&lt;br /&gt;
     also einen Wert und verwirft diesen, um den ADC &amp;quot;warmlaufen zu lassen&amp;quot; */&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
     ;     // auf Abschluss der Konvertierung warten &lt;br /&gt;
  }&lt;br /&gt;
  result = ADCW;  // ADCW muss einmal gelesen werden,&lt;br /&gt;
                  // sonst wird Ergebnis der nächsten Wandlung&lt;br /&gt;
                  // nicht übernommen.&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  result = 0; &lt;br /&gt;
  for( i=0; i&amp;lt;4; i++ )&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);            // eine Wandlung &amp;quot;single conversion&amp;quot;&lt;br /&gt;
    while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
      ;   // auf Abschluss der Konvertierung warten&lt;br /&gt;
    }&lt;br /&gt;
    result += ADCW;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Beispielaufrufe: */&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
&lt;br /&gt;
  adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -&amp;gt; Channel 0 */&lt;br /&gt;
  ...&lt;br /&gt;
  adcval = ReadChannel(2); /* MUX-Bits auf 0b0010 -&amp;gt; Channel 2 */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekennzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Das Löschen des ADIF-Flags sollte, &#039;&#039;&#039;entgegen&#039;&#039;&#039; der [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_intbits FAQ], mit&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADIF);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
erfolgen. Die Methode in der FAQ eignet sich nur für Register in denen &#039;&#039;&#039;nur&#039;&#039;&#039; Interrupt-Flags stehen.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Analog-Digital-Wandlung ohne internen ADC ===&lt;br /&gt;
&lt;br /&gt;
==== Messen eines Widerstandes ====&lt;br /&gt;
&lt;br /&gt;
Analoge Werte lassen sich ohne Analog-Digital-Wandler auch indirekt ermitteln. Im Folgenden wird die Messung des an einem Potentiometer eingestellten Widerstands anhand der Ladekurve eines Kondensators erläutert. Bei dieser Methode wird nur ein Portpin benötigt, ein Analog-Digital-Wandler oder Analog-Comparator ist nicht erforderlich. Es wird dazu ein Kondensator und der Widerstand (das Potentiometer) in Reihe zwischen Vorsorgungsspannung und Masse/GND geschaltet (sogen. RC-Netzwerk). Zusätzlich wird eine Verbindung der Leitung zwischen Kondensator und Potentiometer zu einem Portpin des Controllers hergestellt. Die folgende Abbildung verdeutlicht die erforderliche Schaltung. &lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wird der Portpin des Controllers auf Ausgang konfiguriert (im Beispiel &#039;&#039;DDRD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) und dieser Ausgang auf Logisch 1 (&amp;quot;High&amp;quot;, &#039;&#039;PORTD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) geschaltet, liegt an beiden &amp;quot;Platten&amp;quot; des Kondensators das gleiche Potential &#039;&#039;&#039;VCC&#039;&#039;&#039; an und der Kondensator somit entladen. (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so, da an beiden Seiten des Kondensators das gleiche Potential anliegt und somit eine Potentialdifferenz von 0V besteht =&amp;gt; Kondensator ist entladen).&lt;br /&gt;
&lt;br /&gt;
Nach einer gewissen Zeit ist der Kondensator entladen und der Portpin wird als Eingang konfiguriert (&#039;&#039;DDRD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2); PORTD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2)&#039;&#039;), wodurch dieser hochohmig wird. Der Status des Eingangspin (in PIND) ist Logisch 1 (High). Der Kondensator lädt sich jetzt über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), wird das Eingangssignal als LOW erkannt (Bit in PIND wird 0). Die Zeitspanne zwischen der Umschaltung von Entladung auf Aufladung und dem Wechsel des Eingangssignals von High auf Low ist ein Maß für den am Potentiometer eingestellten Widerstand. Zur Zeitmessung kann einer der im Controller vorhandenen Timer genutzt werden. Der 220 Ohm Widerstand dient dem Schutz des Controllers. Es würde sonst bei Maximaleinstellung des Potentionmeters (hier 0 Ohm) ein zu hoher Strom fließen, der die Ausgangsstufe des Controllers zerstört. &lt;br /&gt;
&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B: 0...100 % oder so) umzurechnen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Link 404 =&amp;gt; auskommentiert, mthomas 9.2.2008 &lt;br /&gt;
Ein Beispielprogramm findet sich auf [http://www.mypage.bluewin.ch/ch_schifferle/ Christian Schifferles Web-Seite] im Archiv &#039;&#039;ATMEL.ZIP&#039;&#039;, welches unter den Titel &#039;&#039;Tutorial &amp;quot;Programmieren mit C für Atmel Mikrocontroller&#039;&#039; heruntergeladen werden kann. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ADC über Komparator ====&lt;br /&gt;
&lt;br /&gt;
Es gibt einen weiteren Weg, eine analoge Spannung mit Hilfe des&lt;br /&gt;
Komparators, welcher in fast jedem AVR integriert ist, zu messen. Siehe dazu&lt;br /&gt;
auch die Application Note AVR400 von Atmel.&lt;br /&gt;
&lt;br /&gt;
Dabei wird das zu messende Signal auf den invertierenden Eingang&lt;br /&gt;
des Komparators geführt. Zusätzlich wird ein Referenzsignal an den nicht&lt;br /&gt;
invertierenden Eingang des Komparators angeschlossen. Das Referenzsignal wird&lt;br /&gt;
hier auch wieder über ein RC-Glied erzeugt, allerdings mit festen Werten für R&lt;br /&gt;
und C.&lt;br /&gt;
&lt;br /&gt;
[[Image:ADC ueber Komparator.gif]]&lt;br /&gt;
&lt;br /&gt;
Das Prinzip der Messung ist nun dem vorhergehenden recht&lt;br /&gt;
ähnlich. Durch Anlegen eines LOW-Pegels an Pin 2 wird der Kondensator zuerst&lt;br /&gt;
einmal entladen. Auch hier muss darauf geachtet werden, dass der Entladevorgang&lt;br /&gt;
genügend lang dauert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Nun wird Pin 2 auf HIGH gelegt. Der Kondensator wird geladen. Wenn die Spannung&lt;br /&gt;
über dem Kondensator die am Eingangspin anliegende Spannung erreicht hat&lt;br /&gt;
schaltet der Komparator durch. Die Zeit, welche benötigt wird, um den&lt;br /&gt;
Kondensator zu laden kann nun auch wieder als Maß für die Spannung an Pin 1&lt;br /&gt;
herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Ich habe es mir gespart, diese Schaltung auch aufzubauen und&lt;br /&gt;
zwar aus mehreren Gründen:&lt;br /&gt;
&lt;br /&gt;
# 3 Pins notwendig.&lt;br /&gt;
# Genauigkeit vergleichbar mit einfacherer Lösung.&lt;br /&gt;
# War einfach zu faul.&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung liegt allerdings darin, dass damit&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines Digital-Analog-Konverters (&#039;&#039;&#039;DAC&#039;&#039;&#039;) können wir nun auch Analogsignale ausgeben. Es gibt hier mehrere Verfahren. &amp;lt;!-- Wenn wir beim ADC die Möglichkeit haben, mit externen Komponenten zu operieren, müssen wir bei der DAC-Wandlung mit dem auskommen, was der Controller selber zu bieten hat. --mt: hmm, richtig? verstaendlich? redundant? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DAC über mehrere digitale Ausgänge ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir an den Ausgängen des Controllers ein entsprechendes&lt;br /&gt;
Widerstandsnetzwerk aufbauen haben wir die Möglichkeit, durch die Ansteuerung&lt;br /&gt;
der Ausgänge über den Widerständen einen Addierer aufzubauen, mit dessen&lt;br /&gt;
Hilfe wir eine dem Zahlenwert proportionale Spannung erzeugen können. Das&lt;br /&gt;
Schaltbild dazu kann etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
[[Image:DAC R2R.gif]]&lt;br /&gt;
&lt;br /&gt;
Es sollten selbstverständlich möglichst genaue Widerstände verwendet&lt;br /&gt;
werden, also nicht unbedingt solche mit einer Toleranz von 10% oder mehr.&lt;br /&gt;
Weiterhin empfiehlt es sich, je nach Anwendung den Ausgangsstrom über einen&lt;br /&gt;
Operationsverstärker zu verstärken.&lt;br /&gt;
&lt;br /&gt;
=== PWM (Pulsweitenmodulation) ===&lt;br /&gt;
&lt;br /&gt;
Wir kommen nun zu einem Thema, welches in aller Munde ist, aber viele&lt;br /&gt;
Anwender verstehen nicht ganz, wie &#039;&#039;&#039; PWM&#039;&#039;&#039; eigentlich funktioniert.&lt;br /&gt;
&lt;br /&gt;
Wie wir alle wissen, ist ein Mikrocontroller ein rein digitales Bauteil.&lt;br /&gt;
Definieren wir einen Pin als Ausgang, dann können wir diesen Ausgang entweder&lt;br /&gt;
auf HIGH setzen, worauf am Ausgang die Versorgungsspannung &#039;&#039;&#039; Vcc&#039;&#039;&#039; anliegt, oder aber wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt. Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen HIGH und LOW umschalten? - Richtig, wir erhalten eine Rechteckspannung, wie die folgende Abbildung zeigt:&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 1.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Rechteckspannung hat nun einen arithmetischen Mittelwert, &lt;br /&gt;
der je nach Pulsbreite kleiner oder größer ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 2.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun diese pulsierende Ausgangsspannung noch über ein RC-Glied filtern/&amp;quot;glätten&amp;quot;, dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVRs können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. &lt;br /&gt;
Dazu dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt, weshalb ein Blick ins entsprechende Datenblatt dringend angeraten wird.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren, müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| PWM-Modus des Timers ist nicht aktiv.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Timer/Counter zählt nun permanent von 0 bis zur Obergrenze&lt;br /&gt;
und wieder zurück, er wird also als sogenannter Auf-/Ab Zähler betrieben. &lt;br /&gt;
Die Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss mit den Bits &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; desselben&lt;br /&gt;
Registers die gewünschte Ausgabeart des Signals definiert werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Nicht invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Hochzählen und gesetzt beim&lt;br /&gt;
Herunterzählen.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Herunterzählen und gesetzt beim&lt;br /&gt;
Hochzählen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl, um beispielsweise den Timer/Counter als&lt;br /&gt;
nicht invertierenden 10-Bit PWM zu verwenden, heißt dann:&lt;br /&gt;
&lt;br /&gt;
alte Schreibweise (PWMxx wird nicht mehr akzeptiert)&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;PWM11)|(1&amp;lt;&amp;lt;PWM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
neue Schreibweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;WGM11)|(1&amp;lt;&amp;lt;WGM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Timer/Counter überhaupt läuft, müssen wir im Control&lt;br /&gt;
Register B &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; noch den gewünschten Takt (Vorteiler) einstellen und&lt;br /&gt;
somit auch die Frequenz des &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signals bestimmen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stop. Der Timer/Counter wird gestoppt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin 1, negative Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin 1, positive Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Also um einen Takt von CK / 1024 zu generieren, verwenden wir&lt;br /&gt;
folgenden Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1B = (1&amp;lt;&amp;lt;CS12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt muss nur noch der Vergleichswert festgelegt werden. Diesen&lt;br /&gt;
schreiben wir in das 16-Bit Timer/Counter Output Compare Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem Vergleichswert und dem generierten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal aufzeigen.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
Ach ja, fast hätte ich&#039;s vergessen. Das generierte &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal&lt;br /&gt;
wird am Output Compare Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; des Timers ausgegeben und leider können wir&lt;br /&gt;
deshalb auch beim AT90S2313 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren. Andere AVR-Typen verfügen über bis zu vier PWM-Ausgänge. Zu beachten ist außerdem, das wenn der OC Pin aktiviert ist, er nichtmehr wie üblich funktioniert und z.B. nicht einfach über PINx ausgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Ein Programm, dass an einem ATMega8 den Fast-PWM Modus verwendet, den Modus 14, könnte so aussehen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // OC1A auf Ausgang&lt;br /&gt;
  DDRB = (1 &amp;lt;&amp;lt; PB1 );&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // Timer 1 einstellen&lt;br /&gt;
  //  &lt;br /&gt;
  // Modus 14:&lt;br /&gt;
  //    Fast PWM, Top von ICR1&lt;br /&gt;
  //&lt;br /&gt;
  //     WGM13    WGM12   WGM11    WGM10&lt;br /&gt;
  //      1        1       1        0&lt;br /&gt;
  //&lt;br /&gt;
  //    Timer Vorteiler: 1&lt;br /&gt;
  //     CS12     CS11    CS10&lt;br /&gt;
  //       0        0       1&lt;br /&gt;
  //&lt;br /&gt;
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match&lt;br /&gt;
  //     COM1A1   COM1A0&lt;br /&gt;
  //       1        0&lt;br /&gt;
 &lt;br /&gt;
  TCCR1A = (1&amp;lt;&amp;lt;COM1A1) | (1&amp;lt;&amp;lt;WGM11);&lt;br /&gt;
  TCCR1B = (1&amp;lt;&amp;lt;WGM13) | (1&amp;lt;&amp;lt;WGM12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  //  den Endwert (TOP) für den Zähler setzen&lt;br /&gt;
  //  der Zähler zählt bis zu diesem Wert&lt;br /&gt;
&lt;br /&gt;
  ICR1 = 0x6FFF;&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  // der Compare Wert&lt;br /&gt;
  // Wenn der Zähler diesen Wert erreicht, wird mit&lt;br /&gt;
  // obiger Konfiguration der OC1A Ausgang abgeschaltet&lt;br /&gt;
  // Sobald der Zähler wieder bei 0 startet, wird der&lt;br /&gt;
  // Ausgang wieder auf 1 gesetzt&lt;br /&gt;
  //&lt;br /&gt;
  // Durch Verändern dieses Wertes, werden die unterschiedlichen&lt;br /&gt;
  // PWM Werte eingestellt.&lt;br /&gt;
&lt;br /&gt;
  OCR1A = 0x3FFF;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  while( 1 )&lt;br /&gt;
    ;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für Details der PWM Möglichkeiten, muß immer das jeweilge Datenblatt des Prozessors konsultiert werden, da sich die unterschiedlichen Prozessoren in ihren Möglichkeiten doch stark unterscheiden. Auch muß man aufpassen, welches zu setzende Bit in welchem Register sind. Auch hier kann es sein, dass gleichnamige Konfigurationsbits in unterschiedlichen Konfigurationsregistern (je nach konkretem Prozessortyp) sitzen.&lt;br /&gt;
&lt;br /&gt;
= LCD-Ansteuerung =&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung an der LCD-Controller-Platine ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; (ist in manchen Unterlagen auch &#039;&#039;&#039;EN&#039;&#039;&#039;  für &#039;&#039;Enable&#039;&#039; abgekürzt) benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert, ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitetet hat (diese Methode u.a. in der LCD-Library von Peter Fleury verwendet). Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt da wir wissen, welche Anschlüsse das LCDs benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin #-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin-µC&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND oder Poti (siehe oben)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD4 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD5 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD0 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD1 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD2 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD3 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man die Steuerleitungen EN und RS auf Pins an einem anderen Port legen möchte, kann man so wie in diesem [http://www.mikrocontroller.net/topic/88543#751982 Forumsbeitrag] vorgehen.&lt;br /&gt;
&lt;br /&gt;
Ok, alles ist verbunden, wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display? &lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
void lcd_data(unsigned char temp1);&lt;br /&gt;
void lcd_string(char *data);&lt;br /&gt;
void lcd_command(unsigned char temp1);&lt;br /&gt;
void lcd_enable(void);&lt;br /&gt;
void lcd_init(void);&lt;br /&gt;
void lcd_home(void);&lt;br /&gt;
void lcd_clear(void);&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y);&lt;br /&gt;
&lt;br /&gt;
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
&lt;br /&gt;
// LCD Befehle&lt;br /&gt;
&lt;br /&gt;
#define CLEAR_DISPLAY 0x01&lt;br /&gt;
#define CURSOR_HOME   0x02&lt;br /&gt;
&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
&lt;br /&gt;
#define LCD_PORT      PORTD&lt;br /&gt;
#define LCD_DDR       DDRD&lt;br /&gt;
#define LCD_RS        PD4&lt;br /&gt;
#define LCD_EN        PD5&lt;br /&gt;
// DB4 bis DB7 des LCD sind mit PD0 bis PD3 des AVR verbunden&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// sendet ein Datenbyte an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_data(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_RS);        // RS auf 1 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_command(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);        // RS auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;              // oberes Nibble holen&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;            // maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;            // unteres Nibble holen und maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// erzeugt den Enable-Puls&lt;br /&gt;
void lcd_enable(void)&lt;br /&gt;
{&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
    _delay_us(1);                   // kurze Pause&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/80900&lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Initialisierung: &lt;br /&gt;
// Muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
void lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
   LCD_DDR = LCD_DDR | 0x0F | (1&amp;lt;&amp;lt;LCD_RS) | (1&amp;lt;&amp;lt;LCD_EN);   // Port auf Ausgang schalten&lt;br /&gt;
&lt;br /&gt;
   // muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(15);&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x03;            &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);      // RS auf 0&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4 Bit Modus aktivieren &lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x02;&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4Bit / 2 Zeilen / 5x7&lt;br /&gt;
   lcd_command(0x28);&lt;br /&gt;
    &lt;br /&gt;
   // Display ein / Cursor aus / kein Blinken&lt;br /&gt;
   lcd_command(0x0C); &lt;br /&gt;
 &lt;br /&gt;
   // inkrement / kein Scrollen&lt;br /&gt;
   lcd_command(0x06);&lt;br /&gt;
&lt;br /&gt;
   lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
&lt;br /&gt;
void lcd_clear(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CLEAR_DISPLAY);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
&lt;br /&gt;
void lcd_home(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CURSOR_HOME);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)&lt;br /&gt;
&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t tmp;&lt;br /&gt;
&lt;br /&gt;
  switch (y) {&lt;br /&gt;
    case 1: tmp=0x80+0x00+x; break;    // 1. Zeile&lt;br /&gt;
    case 2: tmp=0x80+0x40+x; break;    // 2. Zeile&lt;br /&gt;
    case 3: tmp=0x80+0x10+x; break;    // 3. Zeile&lt;br /&gt;
    case 4: tmp=0x80+0x50+x; break;    // 4. Zeile&lt;br /&gt;
  }&lt;br /&gt;
  lcd_command(tmp);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_string(char *data)&lt;br /&gt;
{&lt;br /&gt;
    while(*data) {&lt;br /&gt;
        lcd_data(*data);&lt;br /&gt;
        data++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches die Funktionen benutzt, sieht zb. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen&lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    lcd_data(&#039;T&#039;);&lt;br /&gt;
    lcd_data(&#039;e&#039;);&lt;br /&gt;
    lcd_data(&#039;s&#039;);&lt;br /&gt;
    lcd_data(&#039;t&#039;);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
&lt;br /&gt;
    lcd_string(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist dabei, dass die Optimierung bei der Compilierung eingeschaltet ist, sonst stimmen die Zeiten der Funktionen _delay_us() und _delay_ms() nicht und der Code wird wesentlich länger (Siehe Dokumentation der libc im WinAVR).&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches eine Variable ausgibt, sieht zb. so aus.&lt;br /&gt;
Mittels der itoa() Funktion (itoa = &amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;nteger &amp;lt;b&amp;gt;To&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;A&amp;lt;/b&amp;gt;scii ) wird von einem Zahlenwert eine textuelle Repräsentierung ermittelt (sprich: ein String erzeugt) und dieser String mit der bereits vorhandenen Funktion lcd_string ausgegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen &lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Beispiel&lt;br /&gt;
int variable = 42;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe des Zeichens dessen ASCII-Code gleich dem Variablenwert ist&lt;br /&gt;
    // (Im Beispiel entspricht der ASCII-Code 42 dem Zeichen *)&lt;br /&gt;
    // http://www.code-knacker.de/ascii.htm&lt;br /&gt;
    lcd_data(variable);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
 &lt;br /&gt;
    // Ausgabe der Variable als Text in dezimaler Schreibweise&lt;br /&gt;
    {&lt;br /&gt;
       // ... umwandeln siehe FAQ Artikel bei www.mikrocontroller.net&lt;br /&gt;
       // WinAVR hat eine itoa()-Funktion, das erfordert obiges #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
       char Buffer[20]; // in diesem {} lokal&lt;br /&gt;
       itoa( variable, Buffer, 10 ); &lt;br /&gt;
&lt;br /&gt;
       // ... ausgeben  &lt;br /&gt;
       lcd_string( Buffer );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einrichten eines Projekts muss man zu der Datei mit dem Hauptprogramm auch die Datei lcd-routines.c in das Projekt aufnehmen. Dies geschieht beim AVR Studio unter Source Files im Fenster AVR GCC oder bei WinAVR im Makefile (z.B. durch SRC += lcd-routines.c).&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVRs sind für viele Steuerungsaufgaben zu schnell. Wenn wir beispielsweise eine LED oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die CPU-Frequenz verwenden, da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, Vorgänge in Zeitabständen durchzuführen, die geringer als die Taktfrequenz des Controllers sind. Selbstverständlich sollte die resultierende Frequenz auch noch möglichst genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist ganz einfach ein bestimmtes Register im µC, das völlig ohne Zutun des Programms, also per Hardware, hochgezählt wird. Das alleine wäre noch nicht allzu nützlich, wenn nicht dieses Hardwareregister bei bestimmten Zählerständen einen Interrupt auslösen könnte. Ein solches Ereignis ist der Overflow: Da die Bitbreite des Registers beschränkt ist, kommt es natürlich auch vor, dass der Zähler so hoch zählt, dass der nächste Zählerstand mit dieser Bitbreite nicht mehr darstellbar ist und der Zähler wieder auf 0 zurückgesetzt wird. Dieses Ereignis nennt man den Overflow und es ist möglich an dieses Ereignis einen Interrupt zu koppeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Alternativvorschlag mthomas &lt;br /&gt;
Jeder Timer verfügt über ein Zählerregister im Mikrocontroller, das automatisch und ohne Zutun des Programms von der Hardware weitergezählt wird. In einem einfachen Anwendungsfall stellt man den Timer auf eine Zählgeschwindigkeit (Frequenz) und kann dann anhand des Zählerstands ermitteln, wie viel Zeit vergangen ist. Das eigentlich Nützliche an Timern ist jedoch, dass man  bestimmte Zählerstände mit Interrupts verknüpfen kann, so dass der Controller beim Auftreten automatisch eine vom Anwender geschriebene Routine aufruft. Eines dieser möglichen Ereignis ist der Overflow ((Zähler-)Überlauf), der dann auftritt, wenn der Wert des Zählerregisters (Timer/Counter-Register) den maximal möglichen Wert überschreitet. Der Maximalwert wird durch die Bitbreite des Zählerregisters bestimmt (z.B. 255 bei 8-Bit Timern). Beim Überlauf/Overflow wird der Zähler durch die Hardware auf 0 zurückgesetzt und die Zählung beginnt von neuem. Wurde vorher der Overflow-Interrupt für den Timer aktiviert (im Timer Control Register) unterbricht der Controller automatisch die Ausführung des Hauptprogramms und verzweigt in die Interrupt-Routine des Anwenders.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auf den AT90S2313. Für andere Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer Auflösung von 65536. Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz, der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
== Der Vorteiler (Prescaler) ==&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler dient dazu, den CPU-Takt vorerst um einen einstellbaren Faktor zu reduzieren. Die so geteilte Frequenz wird den Eingängen der Timer zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024 einstellen, wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca. 4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw. Zählregister (TCNTx) mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle verfügen über mindestens einen, teilweise sogar zwei, 8-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird z.B bei AT90S2313 über folgende Register angesprochen (bei anderen Typen weitestgehend analog):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    TCCR0 |= (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst und die daran gebundene Interrupt-Routine abgearbeitet. Das TOV Flag &lt;br /&gt;
lässt sich durch das Hineinschreiben einer 1 und nicht wie erwartet einer 0 wieder zurücksetzen.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen außer den 8-Bit Timern auch 16-Bit Timer. Die 16-Bit Timer/Counter sind etwas komplexer aufgebaut als die 8-Bit Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM13&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM12 (CTC1)&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorteiler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt, und C der voreingestellte Vergleichswert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin T1, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin T1, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T1 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T1 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0, bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;TCNT1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;OCR1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;OCR1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;OCR1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;OCR1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäß folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]] [[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Ausserdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr. Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen kleiner 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4us warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.6 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t x;&lt;br /&gt;
&lt;br /&gt;
    x = 10;&lt;br /&gt;
&lt;br /&gt;
    while (x &amp;gt;= 0)&lt;br /&gt;
    {&lt;br /&gt;
      // tu was&lt;br /&gt;
&lt;br /&gt;
      x--;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog? ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Einige Controller haben einen eigenen Watchdog Oszillator, z.B. der Tiny2313 mit 128kHz. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde, beginnt der Counter von 0 an hochzuzählen. &lt;br /&gt;
Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern, müssen wir den Watchdog regelmäßig wieder neu starten bzw. rücksetzen (Watchdog Reset). &lt;br /&gt;
Dies sollte innerhalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern, muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten. Es müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. &lt;br /&gt;
Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist, wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei &#039;&#039;wdt.h&#039;&#039; (&#039;&#039;#include &amp;lt;avr/wdt.h&amp;gt;&#039;&#039;) in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch gestartet wird. &lt;br /&gt;
Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. &lt;br /&gt;
Falls ein anderer Wert gewünscht ist, so kann dies im Makfile in den Linker-Optionen eingetragen werden. &lt;br /&gt;
Dazu muss in der Zeile LDFLAGS folgende Option angefügt werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert&lt;br /&gt;
:Mögliche Timeoutwerte:&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Konstante&lt;br /&gt;
! Wert&lt;br /&gt;
! TimeOut&lt;br /&gt;
|- &lt;br /&gt;
| WDTO_15MS   &lt;br /&gt;
| 0&lt;br /&gt;
| 15 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_30MS   &lt;br /&gt;
| 1&lt;br /&gt;
| 30 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_60MS   &lt;br /&gt;
| 2&lt;br /&gt;
| 60 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_120MS   &lt;br /&gt;
| 3&lt;br /&gt;
| 120 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_250MS   &lt;br /&gt;
| 4&lt;br /&gt;
| 250 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_500MS   &lt;br /&gt;
| 5&lt;br /&gt;
| 500 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_1S   &lt;br /&gt;
| 6&lt;br /&gt;
| 1 S&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_2S   &lt;br /&gt;
| 7&lt;br /&gt;
| 2s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
== Watchdog-Anwendungshinweise ==&lt;br /&gt;
&lt;br /&gt;
Ob nun der Watchdog als Schutzfunktion überhaupt verwendet werden soll, hängt stark von der Anwendung, der genutzten Peripherie und dem Umfang und der Qualitätssicherung des Codes ab. Will man sicher gehen, dass ein Programm sich nicht in einer Endlosschleife verfängt, ist der Wachdog das geeignete Mittel dies zu verhindern. Weiterhin kann bei geschickter Programmierung der Watchdog dazu genutzt werden, bestimmte Stromsparfunktionen zu implementieren. Bei einigen neueren AVRs (z.B. dem ATTiny13) kann der Watchdog auch direkt als Timer genutzt werden, der den Controller aus einem Schlafmodus aufweckt. Auch dies kann im &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register eingestellt werden. Außerdem bietet der WD die einzige Möglichkeit einen beabsichtigten System-Reset (ein &amp;quot;richtiger Reset&amp;quot;, kein &amp;quot;jmp 0x0000&amp;quot;) ohne externe Beschaltung auszulösen, was z.B. bei der Implementierung eines Bootloaders nützlich ist. Bei bestimmten Anwendungen kann die Nutzung des WD als &amp;quot;ultimative Deadlock-Sicherung für nicht bedachte Zustände&amp;quot; natürlich immer als zusätzliche Sicherung dienen. &lt;br /&gt;
&lt;br /&gt;
Es besteht die Möglichkeit herauszufinden, ob ein Reset durch den Watchdog ausgelöst wurde (beim ATmega16 z.B. Bit WDRF in MCUCSR). Diese Information sollte auch genutzt werden, falls ein WD-Reset in der Anwendung nicht planmäßig implementiert wurde. Zum Beispiel kann man eine LED an einen freien Pin hängen, die nur bei einem Reset durch den WD aufleuchtet oder aber das &amp;quot;Ereignis WD-Reset&amp;quot; im internen EEPROM des AVR absichern, um die Information später z.B. über UART oder ein Display auszugeben (oder einfach den EEPROM-Inhalt über die ISP/JTAG-Schnittstelle auslesen).&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/77273#642501 Bug in ATtiny2313?]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt der Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft. Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe Abschnitt (TODO: verlinken) im Anhang.&lt;br /&gt;
&amp;lt;!--Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. vgl. [[AVR-GCC-Tutorial#Anhang|Anhang]])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.B. &#039;&#039;void TIMER0_OVF_vect(void)...&#039;&#039;). Der Unterschied im Vergleich zu ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben.  &amp;lt;!--Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)), als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur &lt;br /&gt;
in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechnungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechnungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0, PORTA ist danach 0b00000001. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-oder-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8 (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten &#039;&#039;Sleep-Modes&#039;&#039; (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_enable()&#039;&#039;&#039;&lt;br /&gt;
:aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_cpu()&#039;&#039;&#039;&lt;br /&gt;
: Versetzt den Controller in den Schlafmodus (sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt)&lt;br /&gt;
* &#039;&#039;&#039;sleep_disable()&#039;&#039;&#039;&lt;br /&gt;
:deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts.&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;allozieren&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich, um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory, bei AVR Controllern mit mehr als 64kB Flash auch elpm). Die Standard-Laufzeitbibliothek des avr-gcc (die avr-libc) stellt diese Funktionen nach Einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Lokale Variablen (eigentlich Konstanten) innerhalb von Funktionen können ebenfalls im Programmspeicher abgelegt werden. Dazu ist bei der Definition jedoch ein &#039;&#039;static&#039;&#039; voranzustellen, da solche &amp;quot;Variablen&amp;quot; nicht auf dem Stack bzw. (bei Optimierung) in Registern verwaltet werden können. Der Compiler &amp;quot;wirft&amp;quot; eine Warnung falls static fehlt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray2 };&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  static /*const*/ uint8_t pgmTestByteLocal PROGMEM = 0x55;&lt;br /&gt;
  static /*const*/ char pgmTestStringLocal[] PROGMEM = &amp;quot;im Flash&amp;quot;;&lt;br /&gt;
  // so nicht (static fehlt): char pgmTestStringLocalFalsch [] PROGMEM = &amp;quot;so nicht&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 // ...&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
    //...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte....&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWort);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;groß&amp;quot; (Stand avr-gcc 3.4.x). Damit ist der mögliche Speicherbereich für &amp;quot;Flash-Konstanten&amp;quot; auf 64kB begrenzt.  &amp;lt;!-- Einige avr-libc/pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher) (intern via Assembler Anweisung ELPM). Die Initialisierungswerde des Speicherinhalts jenseits der 64kB-Marke müssen dann jedoch auf anderem Weg angelegt werden (nicht PROGMEM, evtl. eigene Section und Linker-Optionen - TODO) /// alt - und nicht ganz korrekt: (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.)--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray enthält nun die Startadresse des Byte-Arrays pgmFooByteArray1&lt;br /&gt;
    // Allerdings würde ein direkter Zugriff mit diesem Pointer (z.B. temp=*ptrToArray)&lt;br /&gt;
    // &#039;&#039;&#039;nicht&#039;&#039;&#039; den Inhalt von pgmFooByteArray1[0] liefern, sondern von einer Speicherstelle&lt;br /&gt;
    // im &#039;&#039;&#039;RAM&#039;&#039;&#039;, die die gleiche Adresse hat wie pgmFooByteArray1[0]&lt;br /&gt;
    // Daher muss nun die Funktion pgm_read_byte() benutzt werden, die die in ptrToArray&lt;br /&gt;
    // enthaltene Adresse benutzt und auf das Flash zugreift.&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray enthält nun die Adresse des ersten Elements des Byte-Arrays pgmFooByteArray2&lt;br /&gt;
    // da im zweiten Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmFooByteArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Array aus Zeichenketten im Flash-Speicher ===&lt;br /&gt;
&lt;br /&gt;
Felder aus Zeichenketten im Flash-Speicher werden in zwei Schritten angelegt: Zuerst die einzelnen Elemente des Arrays und im Anschluss ein Array, in dem die Addressen der Zeichenketten abgelegt werden. Zum Auslesen wird zuerst die Adresse des i-ten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, auf das Element (die Zeichenkette) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const char str1[] PROGMEM = &amp;quot;first_A&amp;quot;;&lt;br /&gt;
const char str2[] PROGMEM = &amp;quot;second_A&amp;quot;;&lt;br /&gt;
const char str3[] PROGMEM = &amp;quot;third_A&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char *strarray1[] PROGMEM = {&lt;br /&gt;
	str1,&lt;br /&gt;
	str2,&lt;br /&gt;
	str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	int i, j, l;&lt;br /&gt;
	const char *pstrflash;&lt;br /&gt;
	char work[20], work2[20];&lt;br /&gt;
	// fuer Simulation: per volatile Optimierung verhindern, &lt;br /&gt;
	//                  da c nicht genutzt&lt;br /&gt;
	volatile char c;&lt;br /&gt;
	&lt;br /&gt;
	for ( i = 0; i &amp;lt; (sizeof(strarray1)/sizeof(strarray1[0]) ); i++ ) {&lt;br /&gt;
&lt;br /&gt;
		// setze Pointer auf die Addresse des i-ten Elements des&lt;br /&gt;
		// &amp;quot;Flash-Arrays&amp;quot; (str1, str2, ...)&lt;br /&gt;
		pstrflash = (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) );&lt;br /&gt;
		&lt;br /&gt;
		// kopiere den Inhalt der Zeichenkette von der&lt;br /&gt;
		// in pstrflash abgelegten Adresse in das work-Array&lt;br /&gt;
		// analog zu strcpy( work, strarray1[i]) wenn alles im RAM&lt;br /&gt;
		strcpy_P( work, pstrflash );&lt;br /&gt;
		// verkuerzt:&lt;br /&gt;
		strcpy_P( work2, (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) ) );&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
		// Zeichen-fuer-Zeichen&lt;br /&gt;
		l = strlen_P( pstrflash );&lt;br /&gt;
		for ( j=0; j &amp;lt; l; j++ ) {&lt;br /&gt;
			// analog zu c=strarray[i][j] wenn alles im RAM&lt;br /&gt;
			c = (char)( pgm_read_byte( pstrflash++ ) );&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	while (1) { ; }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: &amp;quot;How do I put an array of strings completely in ROM?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5)) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Vorsicht: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char textImFlashOK[] PROGMEM = &amp;quot;mit[]&amp;quot;; &lt;br /&gt;
// = Daten im &amp;quot;Flash&amp;quot;, textImFlashOK* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char* textImFlashProblem PROGMEM = &amp;quot;mit*&amp;quot;;&lt;br /&gt;
// Konflikt: Daten im BSS (lies: RAM), textImFlashFAIL* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der Initialisierungsstring von &amp;quot;textImFlashProblem&amp;quot; zu den Konstanten ans Ende des Programmcodes gelegt wird (BSS), von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte (und wird). Da der lesende Code (mittels pgm_read*) trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein weiters Problem des AVR-GCC (gesehen bei avr-gcc 3.4.1 und 3.4.2) bei der Anpassung an die Harvard-Architektur zu sein (konstanter Pointer auf variable Daten?!). Abhilfe (&amp;quot;Workaround&amp;quot;): Initialisierung bei Zeichenketten mit [] oder gleich im Code PSTR(&amp;quot;...&amp;quot;) nutzen.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden, ob es sich um eine Adresse im Flash oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind.&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die einen im Flash abgelegten String z.B. an eine UART ausgibt, würde dann so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void uart_puts_p(const char *text)&lt;br /&gt;
{&lt;br /&gt;
    char Zeichen;&lt;br /&gt;
&lt;br /&gt;
    while (Zeichen = pgm_read_byte(text))&lt;br /&gt;
    {   /* so lange, wie mittels pgm_read_byte ein Zeichen vom Flash gelesen&lt;br /&gt;
           werden konnte, welches nicht das &amp;quot;String-Endezeichen&amp;quot; darstellt */&lt;br /&gt;
&lt;br /&gt;
        /* Das gelesene Zeichen über die normalen Kanäle verschicken */&lt;br /&gt;
        uart_putc(Zeichen);&lt;br /&gt;
        text++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Flash in der Anwendung schreiben ===&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option (auch bekannt als Bootloader-Support) können Teile des Flash-Speichers auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. Bei wenigen &amp;quot;kleinen&amp;quot; AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm(Flash)- und Datenspeicher(RAM) auf. Der C-Standard und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiß&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, dass vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgegeben sind, die innerhalb weniger Taktzyklen aufeinanderfolgen müssen (&amp;quot;timed sequence&amp;quot;). Auch dies muss bei Nutzung der Funktionen aus der avr-libc/eeprom.h-Datei nicht selbst implementiert werden. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Siehe dazu folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt; // wird in aktuellen Versionen der avr-lib mit xx.h eingebunden&lt;br /&gt;
&lt;br /&gt;
// EEMEM wird bei aktuellen Versionen der avr-lib in eeprom.h definiert&lt;br /&gt;
// hier: definiere falls noch nicht bekannt (&amp;quot;alte&amp;quot; avr-libc)&lt;br /&gt;
#ifndef EEMEM&lt;br /&gt;
// alle Textstellen EEMEM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEMEM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
//...&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // &#039;Variablen&#039; eeFooByte geschrieben&lt;br /&gt;
//...&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z.B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    #define EEPROM_DEF 0xFF&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ( ( myByte = eeprom_read_byte(&amp;amp;eeFooByte) ) == EEPROM_DEF ) {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
//...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprüft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und möglicherweise noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,3);&lt;br /&gt;
&lt;br /&gt;
    /* dito etwas anschaulicher aber &amp;quot;unnütze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fließkommazahlen lassen sich recht praktisch über eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t i[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    uint8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEMEM;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
//...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklaration abgeleiteten EEPROM-Inhalt in eine Datei geschrieben werden (übliche Dateiendung: .eep, Daten im Intel Hex-Format). Damit können recht elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen (siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles). Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden (Write EEPROM), wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse (vgl. Datenblatt Abschnitt Fuse Bits) die vorherigen Daten (EESAVE programmed = 0), deren Position möglicherweise nicht mehr mit der Belegung im aktuellen Programm übereinstimmt oder den Standardwert nach &amp;quot;Chip Erase&amp;quot;: 0xFF (EESAVE unprogrammed = 1). Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Eine besondere Funktion des avr-gcc ist, dass mit entsprechenden Optionen im Makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Variable auf feste Adressen legen ===&lt;br /&gt;
&lt;br /&gt;
Gleich zu Beginn möchte ich darauf hinweisen, dass dieses Verfahren nur ein Workaround ist, mit dem man das Problem der anscheinend &amp;quot;zufälligen&amp;quot; Verteilung&lt;br /&gt;
der EEPROM-Variablen durch den Compiler etwas in den Griff bekommen kann.&lt;br /&gt;
&lt;br /&gt;
Hilfreich kann dies vor allem dann sein, wenn man z.B. über einen Kommandointerpreter (o.ä. Funktionen) direkt bestimmte EEPROM-Adressen manipulieren möchte. Auch wenn man über einen JTAG-Adapter (mk I oder mkII) den Programmablauf manipulieren möchte, indem man die EEPROM-Werte direkt ändert, kann diese Technik hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Im folgenden nun zwei Sourcelistings mit einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.h&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#inlcude &amp;lt;avr/eeprom.h&amp;gt;     // Die EEPROM-Definitionen/Macros der avr-libc einbinden&lt;br /&gt;
&lt;br /&gt;
#define   EESIZE   512      // Maximale Größe des EEPROMS&lt;br /&gt;
&lt;br /&gt;
#define   EE_DUMMY   0x000  // Dummyelement (Adresse 0 sollte nicht genutzt werden)&lt;br /&gt;
#define   EE_VALUE1  0x001  // Eine Bytevariable  &lt;br /&gt;
#define   EE_WORD1L  0x002  // Eine Wordvariable (Lowbyte)&lt;br /&gt;
#define   EE_WORD1H  0x003  // Eine Wordvariable (Highbyte)&lt;br /&gt;
#define   EE_VALUE2  0x004  // Eine weitere Bytevariable&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den Macros &#039;&#039;&#039;#define EE_VALUE1&#039;&#039;&#039; legt man den Namen und die Adresse der&lt;br /&gt;
&#039;Variablen&#039; fest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Die Adressen sollten fortlaufend, zumindest aber aufsteigend sortiert sein! Ansonsten besteht die Gefahr, daß man sehr schnell ein Durcheinander im EEPROM Speicher veranstaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Für den Compiler sind das lediglich Speicher-Adressen, über die auf das EEPROM zugegriffen wird. Der Compiler sieht nichts davon als eine echte Variable an und stößt sich daher auch nicht daran, wenn 2 Makros mit der gleichen Speicheradresse, bzw. überlappenden Speicherbereichen definiert werden. Es liegt einzig und alleine in der Hand des Programmierers, hier keinen Fehler zu machen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
   [EE_DUMMY]   = 0x00,&lt;br /&gt;
   [EE_VALUE1]  = 0x05,&lt;br /&gt;
   [EE_WORD1L]  = 0x01,   &lt;br /&gt;
   [EE_WORD1H]  = 0x00,&lt;br /&gt;
   [EE_VALUE2]  = 0xFF&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch die Verwendung eines Array, welches das gesammte EEPROM umfasst, bleibt&lt;br /&gt;
dem Compiler nicht anderes übrig, als das Array so zu platzieren, dass Element 0&lt;br /&gt;
des Arrays der Adresse 0 des EEPROMs entspricht. (&#039;&#039;Ich hoffe nur, dass die Compilerbauer daran nichts ändern!&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Wie man in dem obigen Codelisting auch sehen kann, hat das Verfahren einen kleinen Haken. Variablen die größer sind als 1 Byte, müssen etwas umständlicher&lt;br /&gt;
definiert werden. Benötigt man keine Initialisierung durch das Programm (was der Normalfall sein dürfte), dann kann man das auch so machen:&lt;br /&gt;
&lt;br /&gt;
Möchte man im EEPROM hintereinander beispielsweise Variablen, mit den Namen &#039;&#039;&#039;Wert&#039;&#039;&#039;, &#039;&#039;&#039;Anzahl&#039;&#039;&#039;, &#039;&#039;&#039;Name&#039;&#039;&#039; und &#039;&#039;&#039;Wertigkeit&#039;&#039;&#039; definieren, wobei Wert und Wertigkeit 1 Byte belegen sollen, Anzahl als 1 Wort (also 2 Bytes) und Name mit 10 Bytes reserviert werden soll, so geht auch folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes Makro definiert also seine Startadresse durch die Startadresse der unmittelbar vorhergehende &#039;Variablen&#039; plus der Anzahl der Bytes die von der vorhergehenden &#039;Variablen&#039; verbraucht werden. Dadurch ist man zumindest etwas auf der sicheren Seite, dass keine 2 &#039;Variablen&#039; im EEPROM überlappend definiert werden. Möchte man eine weitere &#039;Variable&#039; hinzufügen, so wird deren&lt;br /&gt;
Name, einfach anstelle der EE_LAST eingesetzt und eine neue Zeile für EE_LAST eingefügt, in der dann die Größe der &#039;Variablen&#039; festgelegt wird. Zb.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_PROZENT    ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( double ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EE_PROZENT legt die Startadresse für eine neue &#039;Variable&#039; des Datentyps double fest.&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf die EEPROM Werte kann dann z.B.so erfolgen:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t   temp1;&lt;br /&gt;
uint16_t  temp2;&lt;br /&gt;
&lt;br /&gt;
temp1 = eeprom_read_byte(EE_VALUE1);&lt;br /&gt;
temp2 = eeprom_read_word(EE_WORD1L);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ob die in der avr-libc vorhandenen Funktionen dafür verwendet werden können, weiß ich nicht. Aber in einigen Fällen muss man sich sowieso eigene Funktionen&lt;br /&gt;
bauen, welche die spezifischen Anforderungen (Interrupt - Atom Problem, etc.)&lt;br /&gt;
erfüllen.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist nur eine Möglichkeit, wie man dies realisieren kann. Sie bietet einem eine relativ einfache Art die EEPROM-Werte&lt;br /&gt;
auf beliebige Adressen zu legen oder Adressen zu ändern. Die Andere Möglichkeit besteht darin, die EEPROM-Werte wie folgt zu belegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
  0x00,                     //  ee_dummy&lt;br /&gt;
  0x05,                     //  ee_value1&lt;br /&gt;
  0x01,                     //  ee_word1L&lt;br /&gt;
  0x00,                     // (ee_word1H)&lt;br /&gt;
  0xFF                      //  ee_value2&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei kann man Variablen, die größer sind als 1 Byte einfacher definieren und&lt;br /&gt;
man muss nur die Highbyte- oder Lowbyte-Adresse in der &amp;quot;eeprom.h&amp;quot; definieren.&lt;br /&gt;
Allerdings muss man hier höllisch aufpassen, dass man nicht um eine oder mehrere&lt;br /&gt;
Positionen verrutscht!&lt;br /&gt;
&lt;br /&gt;
Welche der beiden Möglichkeiten man einsetzt, hängt vor allem davon ab, wieviele&lt;br /&gt;
Byte, Word und sonstige Variablen man benutzt. Gewöhnen sollte man sich an beide Varianten können ;)&lt;br /&gt;
&lt;br /&gt;
Kleine Schlussbemerkung:&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc unterstützt die Variante 1 und die Variante 2&lt;br /&gt;
* Der icc-avr Compiler unterstützt nur die Variante 2!&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Bei alten Versionen der avr-libc wurden nicht alle AVR Controller  untersützt. Z.B. bei der avr-libc Version 1.2.3 insbesondere bei AVRs &amp;quot;der neuen Generation&amp;quot; (ATmega48/88/168/169) funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register). In neueren Versionen (z.B. avr-libc 1.4.3 aus WinAVR 20050125) wurde die Zahl der unterstüzten Controller deutlich erweitert und eine Methode zur leichten Anpassung an zukünftige Controller eingeführt.&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Will oder kann man nicht auf die neue Version aktualisieren, kann der dort gezeigte Code auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;, gegebenfalls Schutz vor Unterbrechnung/Interrupt ergänzen &#039;&#039;uint8_t sreg; sreg=SREG; cli(); [EEPROM-Code] ; SREG=sreg; return;&#039;&#039;, siehe Abschnitt Interrupts). Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== EEPROM Register ===&lt;br /&gt;
Um das EEPROM anzusteuern sind drei Register von Bedeutung.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEAR Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL da in einem 8 Bit Register keine 512 Adressen adressiert werden können&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEDR Hier werden die Daten eingetragen die geschrieben werden sollen bzw. es enthält die gelesenen Daten&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EECR Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TABLE BORDER=1&amp;gt;&lt;br /&gt;
&amp;lt;TR&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;Bit&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;1&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;0&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&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;Name&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&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;TD&amp;gt;-&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERIE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEMWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERE&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;Read/Write&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;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&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;Init Value&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;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;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;TD&amp;gt;0&amp;lt;/TD&amp;gt;&lt;br /&gt;
&amp;lt;/TR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/TABLE&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bedeutung der Bits:&amp;lt;/U&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4-7 nicht belegt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 3 (EERIE) - EEPROM Ready Interrupt Enable:&amp;lt;/U&amp;gt; Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7) wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0 wird kein Interrupt ausgelöst.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 2(EEMWE) - EEPROM Master Write Enable:&amp;lt;/U&amp;gt; Dieses Bit bestimmt, daß wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE =0 ist und EEWE = 1 gesetzt wird hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst.&lt;br /&gt;
Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 1 (EEWE) - EEPROM Write Enable:&amp;lt;/U&amp;gt; Dieses Bit löst den Schreibvorgang aus wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist wird dieses Bit automatisch wieder auf 0 gesetzt und sofern EERIE gesetzt ist ein Interrupt ausgelöst. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ein Schreibvorgang sieht typischerweise wie folgt aus:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. EEPROM Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Daten übergeben an EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1 &amp;lt;BR&amp;gt;&lt;br /&gt;
5. (Optinal) Warten bis Schreibvorgang abgeschlossen ist.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 0 EERE – EEPROM Read Enable:&amp;lt;/U&amp;gt; Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit wenn das Bit EEWE=0 ist. ISt der LEsevorgang abgeschlossen wird das Bit wieder auf 0 gesetzt und das EEPROM ist für neue Lese/Schreibbefehle wieder bereit.&amp;lt;BR&amp;gt;&lt;br /&gt;
Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. Bereitschaft zum lesen prüfen (EEWE=0)&amp;lt;BR&amp;gt;&lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Lesezyklus auslösen mit EERE = 1&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Warten bis Lesevorgang abgeschlossen EERE = 0&amp;lt;BR&amp;gt;&lt;br /&gt;
5. Daten abholen aus EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bietet sich printf/sprintf an. &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t s[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( s, &amp;quot;Zählerstand: %d&amp;quot;, value );&lt;br /&gt;
    uart_puts( s );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, die Ausgabe stdout auf eigene Ausgabefunktionen umzuleiten. Dazu wird dem Ausgabemechanismus eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben (wie auch immer die Ausgabe stattfindet). Alle anderen, höheren, Funktionen greifen letztendlich auf diese eine Ausgabefunktion zurück, die die Ausgabe eines einzelnen Zeichens vornimmt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uart_init();&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
    printf( &amp;quot;Hello, world!\n&amp;quot; );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil aller printf-Varianten: Sie sind recht speicherintensiv.&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C- und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gnu-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Inline-Assembler: Die Assembleranweisungen werden direkt in den C-Code integriert. Eine Quellcode-Datei enthält somit C- und Assembleranweisungen&lt;br /&gt;
* Assembler-Dateien: Der Assemblercode befindet sich in eigenen Quellcodedateien. Diese werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung (NOP steht für No Operation). Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=Warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
   /* Verzögern der weiteren Programmausführung um&lt;br /&gt;
      genau 3 Taktzyklen */&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeintlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
&lt;br /&gt;
    /* leere Schleife - wird bei eingeschalteter Compiler-Optimierung   wegoptimiert */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* alternative Methode (keine Optimierung): */&lt;br /&gt;
    volatile uint16_t j;&lt;br /&gt;
    for (j = 0; j &amp;lt; 1000; j++)&lt;br /&gt;
      ;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützlicher &amp;quot;Assembler-Einzeiler&amp;quot; ist der Aufruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;);&#039;&#039;), da hierzu in älteren Versionen der avr-libc keine eigene Funktion existiert (in neueren Versionen &#039;&#039;sleep_cpu()&#039;&#039; aus sleep.h).&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise Delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil, dass die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static inline void delayloop16 (uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
    asm volatile (&amp;quot;cp  %A0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;cpc %B0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;breq 2f               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;1:                    \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;sbiw %0,1             \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;brne 1b               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;2:                    &amp;quot;  &lt;br /&gt;
                  : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
	          : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
    );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Anweisung wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen. Der Zeilenumbruch teilt dem Assembler mit, dass ein neuer Befehl beginnt.&lt;br /&gt;
* Als Sprung-Marken (Labels) werden Ziffern verwendet. Diese speziellen Labels sind mehrfach im Code verwendbar. Gesprungen wird jeweils zurück (b) oder vorwärts (f) zum nächsten ausffindbaren Label.&lt;br /&gt;
&lt;br /&gt;
Das Resultat zeigt ein Blick in die Assembler-Datei, die der Compiler mit der option &amp;lt;tt&amp;gt;-save-temps&amp;lt;/tt&amp;gt; nicht löscht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	cp  r24, __zero_reg__ 	 ;  count&lt;br /&gt;
	cpc r25, __zero_reg__ 	 ;  count&lt;br /&gt;
	breq 2f               &lt;br /&gt;
	1:                    &lt;br /&gt;
	sbiw r24,1             	 ;  count&lt;br /&gt;
	brne 1b               &lt;br /&gt;
	2:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc im Abschnitt [http://www.nongnu.org/avr-libc/user-manual/inline_asm.html Related Pages/Inline Asm]. &lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc Deutsche Einführung in Inline-Assembler]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
Wenn man mit dem AVR Studio arbeitet, kann alternativ auch das standardmäßig erstellte Makefile bearbeitet und folgende Zeilen eingefügt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
## Objects that must be built in order to link&lt;br /&gt;
OBJECTS = (alte Dateien...) useful.o&lt;br /&gt;
&lt;br /&gt;
## Compile&lt;br /&gt;
## Hier folgt eine Liste der gelinkten Dateien, darunter einfügen:&lt;br /&gt;
useful.o: ../useful.S&lt;br /&gt;
	$(CC) $(INCLUDES) $(ASMFLAGS) -c  $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Das war es schon. Allerdings gilt es zu beachten, dass das makefile über &amp;quot;Project -&amp;gt; Configuration options&amp;quot; selbst einzubinden ist, sonst wird es natürlich wieder überschrieben.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel eine Funktion &#039;&#039;superFunc&#039;&#039;, die alle Pins des Ports D auf &amp;quot;Ausgang&amp;quot; schaltet, eine Funktion &#039;&#039;ultraFunc&#039;&#039;, die die Ausgänge entsprechend des übergebenen Parameters schaltet, eine Funktion &#039;&#039;gigaFunc&#039;&#039;, die den Status von Port A zurückgibt und eine Funktion &#039;&#039;addFunc&#039;&#039;, die zwei Bytes zu einem 16-bit-Wort addiert. Die Zuweisungen im C-Code (PORTx = ...) verhindern, dass der Compiler die Aufrufe wegoptimiert und dienen nur zur Veranschaulichung der Parameterübergaben.&lt;br /&gt;
&lt;br /&gt;
Zuerst der Assembler-Code. Der Dateiname sei useful.S:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//; Arbeitsregister (ohne &amp;quot;r&amp;quot;) &lt;br /&gt;
workreg  = 16&lt;br /&gt;
workreg2 = 17&lt;br /&gt;
&lt;br /&gt;
//; Konstante:&lt;br /&gt;
ALLOUT = 0xff&lt;br /&gt;
&lt;br /&gt;
//; ** Setze alle Pins von PortD auf Ausgang **&lt;br /&gt;
//; keine Parameter, keine Rückgabe&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg  // beachte: _SFR_IO_ADDR()&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Setze PORTD auf übergebenen Wert **&lt;br /&gt;
//; Parameter in r24 (LSB immer bei &amp;quot;graden&amp;quot; Nummern)&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zustand von PINA zurückgeben **&lt;br /&gt;
//; Rückgabewerte in r24:r25 (LSB:MSB), hier nur LSB genutzt&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zwei Bytes addieren und 16-bit-Wort zurückgeben **&lt;br /&gt;
//; Parameter in r24 (Summand1) und r22 (Summand2) -&lt;br /&gt;
//;  Parameter sind Word-&amp;quot;aligned&amp;quot; d.h. LSB immer auf &amp;quot;graden&amp;quot;&lt;br /&gt;
//;  Registernummern. Bei 8-Bit und 16-Bit Paramtern somit &lt;br /&gt;
//;  beginnend bei r24 dann r22 dann r20 etc.&lt;br /&gt;
//; Rückgabewert in r24:r25&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   push workreg2&lt;br /&gt;
   clr workreg2&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
   pop workreg2&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
//; oh je - sorry - Mein AVR-Assembler ist eingerostet, hoffe das stimmt so...&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Makefile ist der Name der Assembler-Quellcodedatei einzutragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
ASRC = useful.S&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Aufruf erfolgt dann im C-Code so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
extern void superFunc(void);&lt;br /&gt;
extern void ultraFunc(uint8_t setVal);&lt;br /&gt;
extern uint8_t gigaFunc(void);&lt;br /&gt;
extern uint16_t addFunc(uint8_t w1, uint8_t w2);&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
[...]&lt;br /&gt;
  superFunc();&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
&lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
[...]&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird wieder in der lss-Datei ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
   superFunc();&lt;br /&gt;
 148:	0e 94 f6 00 	call	0x1ec&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
 14c:	85 e5       	ldi	r24, 0x55	; 85&lt;br /&gt;
 14e:	0e 94 fb 00 	call	0x1f6&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
 152:	0e 94 fd 00 	call	0x1fa&lt;br /&gt;
 156:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
  &lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
 158:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 15a:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 15c:	0e 94 ff 00 	call	0x1fe&lt;br /&gt;
 160:	8b bb       	out	0x1b, r24	; 27&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
 162:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 164:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 166:	0e 94 fc 00 	call	0x1f8&lt;br /&gt;
 16a:	89 2f       	mov	r24, r25&lt;br /&gt;
 16c:	99 27       	eor	r25, r25&lt;br /&gt;
 16e:	88 bb       	out	0x18, r24	; 24&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
000001ec &amp;lt;superFunc&amp;gt;:&lt;br /&gt;
// setze alle Pins von PortD auf Ausgang&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1ec:	0f 93       	push	r16&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
 1ee:	0f ef       	ldi	r16, 0xFF	; 255&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg&lt;br /&gt;
 1f0:	01 bb       	out	0x11, r16	; 17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 1f2:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 1f4:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001f6 &amp;lt;ultraFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// setze PORTD auf übergebenen Wert&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
 1f6:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
   ret&lt;br /&gt;
 1f8:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fa &amp;lt;gigaFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Zustand von PINA zurückgeben&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
 1fa:	89 b3       	in	r24, 0x19	; 25&lt;br /&gt;
   ret&lt;br /&gt;
 1fc:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fe &amp;lt;addFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// zwei Bytes addieren und 16-bit-Wort zurückgeben&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1fe:	0f 93       	push	r16&lt;br /&gt;
   push workreg2&lt;br /&gt;
 200:	1f 93       	push	r17&lt;br /&gt;
   clr workreg2&lt;br /&gt;
 202:	11 27       	eor	r17, r17&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
 204:	06 2f       	mov	r16, r22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
 206:	08 0f       	add	r16, r24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
 208:	11 1d       	adc	r17, r1&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
 20a:	c8 01       	movw	r24, r16&lt;br /&gt;
   pop workreg2&lt;br /&gt;
 20c:	1f 91       	pop	r17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 20e:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 210:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zuweisung von Registern zu Parameternummer und die Register für die Rückgabewerte sind in den &amp;quot;Register Usage Guidelines&amp;quot; der avr-libc-Dokumentation erläutert.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/assembler.html avr-libc-Dokumentation: Related Pages/avr-libc and assembler programs]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage avr-libc-Dokumentation: Related Pages/FAQ/&amp;quot;What registers are used by the C compiler?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
== Globale Variablen für Datenaustausch ==&lt;br /&gt;
&lt;br /&gt;
Oftmals kommt man um globale Variablen nicht herum, z.B. um den Datenaustausch zwischen Hauptprogramm und Interrupt-Routinen zu realisieren. &lt;br /&gt;
Hierzu muss man im Assembler wissen, wo genau die Variable vom C-Compiler abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
Hierzu muss die Variable, hier &amp;quot;zaehler&amp;quot; genannt, zuerst im C-Code als Global definiert werden, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
int16_t main (void)&lt;br /&gt;
{&lt;br /&gt;
    // irgendein Code, in dem zaehler benutzt werden kann&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im foldenden Assembler-Beispiel wird der Externe Interrupt0  verwendet, um den Zähler hochzuzählen. Es fehlen die Initialisierungen des Interrupts und die Interrupt-Freigabe, so richtig sinnvoll ist der Code auch nicht, aber er zeigt (hoffentlich) wie es geht.&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit Interrupt-Vektoren gilt beim GCC-Assembler das Gleiche, wie bei C: Man muss die exakte Schreibweise beachten, ansonsten wird nicht der Interrupt-Vektor angelegt, sondern eine neue Funktion - und man wundert sich, dass nichts funktionert (vgl. das AVR-GCC-Handbuch).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
.extern zaehler&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      //; wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     //; Status-Register (SREG) sichern!&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     lds temp,zaehler               //; Wert aus dem Speicher lesen&lt;br /&gt;
     inc temp                       //; bearbeiten&lt;br /&gt;
     sts zaehler,temp               //; und wieder zurückschreiben&lt;br /&gt;
&lt;br /&gt;
     pop temp                       //; die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen im Assemblerfile anlegen ===&lt;br /&gt;
&lt;br /&gt;
Alternativ können Variablen aber auch im Assemblerfile angelegt werden. Dadurch kann auf eine .c-Datei verzichtet werden. Für das obige Beispiel könnte der Quelltext dann die Dateien zaehl_asm.S und zaehl_asm.h abgelegt werden, so dass nur noch zaehl_asm.S mit kompiliert werden müsste.&lt;br /&gt;
&lt;br /&gt;
Anstatt im Assemblerfile über das Schlüsselwort &#039;&#039;.extern &#039;&#039; auf eine vorhandene Variable zu verweisen, wird dazu mit dem Schlüsselwort &#039;&#039;.comm&#039;&#039; die benötigte Anzahl von Bytes für eine Variable reserviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.S&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
//; 1 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 1&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Headerdatei wird dann auf die Variable nur noch verwiesen (Schlüsselwort &#039;&#039;extern&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef ZAEHL_ASM_H&lt;br /&gt;
#define ZAEHL_ASM_H&lt;br /&gt;
&lt;br /&gt;
extern volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu globalen Variablen in C werden so angelegte Variablen nicht automatisch mit dem Wert 0 initialisiert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer als 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Variablen, die größer als &#039;&#039;&#039;ein&#039;&#039;&#039; Byte sind, können in Assembler auf ähnliche Art angesprochen werden. Hierzu müssen nur genug Bytes angefordert werden, um die Variable aufzunehmen. Soll z.B. für den Zähler eine Variable vom Typ &#039;&#039;unsigned long&#039;&#039;, also &#039;&#039;uint32_t&#039;&#039; verwendet werden, so müssen 4 Bytes reserviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die dazugehörige Deklaration im Headerfile wäre dann:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
extern volatile uint32_t zaehler;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen, die größer als ein Byte sind, werden die Werte beginnend mit dem niederwertigsten Byte im RAM abgelegt. Das folgende Codeschnippsel zeigt, wie unter Assembler auf die einzelnen Bytes zugegriffen werden kann. Dazu wird im Interrupt nun ein 32-Bit Zähler erhöht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      // wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     // Status-Register (SREG) sichern !&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     // 32-Bit-Zähler incrementieren&lt;br /&gt;
     lds temp, (zaehler + 0)        // 0. Byte (niederwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 0), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
     lds temp, (zaehler + 1)        // 1. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 1), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 2)        // 2. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 2), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 3)        // 3. Byte (höchstwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 3), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
RAUS:&lt;br /&gt;
     pop temp                       // die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TODO:&#039;&#039;&#039; 16-Bit / 32-Bit Variablen, Zugriff auf Arrays (Strings)&lt;br /&gt;
&lt;br /&gt;
= Anhang =&lt;br /&gt;
&lt;br /&gt;
== Besonderheiten bei der Anpassung bestehenden Quellcodes ==&lt;br /&gt;
&lt;br /&gt;
Einige Funktionen, die in frühren Versionen der avr-libc vorhanden waren, werden inzwischen als veraltet angesehen. Sie sind nicht mehr vorhanden oder als &#039;&#039;deprecated&#039;&#039; (missbilligt) ausgewiesen und Definitionen in &amp;lt;compat/deprecated.h&amp;gt; verschoben. Es empfiehlt sich, vorhandenen Code zu portieren und die alten Funktionen nicht mehr zu nutzen, auch wenn diese noch zur Verfügung stehen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zur Deklaration von Interrupt-Routinen ===&lt;br /&gt;
&lt;br /&gt;
Die Funktionen (eigentlich Makros) &#039;&#039;SIGNAL&#039;&#039; und &#039;&#039;INTERRUPT&#039;&#039; zur Deklaration von Interruptroutinen sollten nicht mehr genutzt werden. &lt;br /&gt;
&lt;br /&gt;
In aktuellen Versionen der avr-libc (z.B. avr-libc 1.4.3 aus WinAVR 20060125) werden Interruptroutinen, die &#039;&#039;&#039;nicht&#039;&#039;&#039; durch andere Interrupts &#039;&#039;&#039;unterbrechbar&#039;&#039;&#039; sind, mit ISR deklariert (siehe Abschnitt im Hauptteil). Auch die Benennung wurden vereinheitlicht und an die üblichen Bezeichnungen in den AVR Datenblättern angepasst. In der Dokumentation der avr-libc sind alte und neue Bezeichnungen in der Tabelle gegenübergestellt. Die erforderlichen Schritte zur Portierung:&lt;br /&gt;
&lt;br /&gt;
* #include von avr/signal.h entfernen&lt;br /&gt;
* SIGNAL duch ISR ersetzen&lt;br /&gt;
* Name des Interrupt-Vektors anpassen (SIG_* durch entsprechendes *_vect)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für die Anpassung zuerst ein &amp;quot;alter&amp;quot; Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer2 Output Compare bei einem ATmega8 */&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE2)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt wird der Vektor mit TIMER2 COMP bezeichnet. Die Bezeichnung in der avr-libc entspricht dem Namen im Datenblatt, Leerzeichen werden durch Unterstriche (_) ersetzt und ein _vect angehängt. &lt;br /&gt;
&lt;br /&gt;
Der neue Code sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt; &lt;br /&gt;
/* signal.h entfällt */&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER2_COMP_vect)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Unklarheiten bezüglich der neuen Vektorlabels hilft (noch) ein Blick in die Headerdatei des entsprechenden Controllers. Für das vorherige Beispiel also der Blick in die Datei iom8.h für den ATmega8, dort findet man die veraltete Bezeichnung unterhalb der aktuellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect		_VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2		_VECTOR(3)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Nachfolgendes mit avr-libc 1.4.5 (in WinAVR 1/2007 behoben - noch eine Weile auskommentiert lassen und dann löschen.&lt;br /&gt;
Konnte in alten Versionen signal.h ohne interrupt.h eingebunden werden, erhält man bei Verwendung der avr-libc Version 1.4.3 (WinAVR 2/2005) beim Compilieren eine Fehlermeldung, da mit signal.h nicht die erforderlichen Definitionen eingebunden werden. Der Lösungsvorschlag in signal.h auch interrupt.h einzubinden, wurde von den avr-libc-Enwicklern akzeptiert und das Problem ist  im Quellcode (CVS) bereits behoben. Es ist aber noch keine avr-libc-&amp;quot;Release&amp;quot; bzw. noch kein WinAVR mit dieser avr-libc-Korrektur verfügbar (Stand 5.2.2006). Will oder kann man den Quellcode nicht aktualisieren, gibt es folgende Alternativen:&lt;br /&gt;
* in Quellcodedateien, in denen nur avr/signal.h eingebunden wird, interrupt.h einbinden (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;). signal.h weiterhin einbinden, falls Kompatibiltät mit alten Versionen gewünscht ist.&lt;br /&gt;
* in der Datei signal.h (bein WinAVR in c:/WinAVR/avr/include/avr/signal.h) ein (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;) ergänzen --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;unterbrechbare&#039;&#039;&#039; Interruptroutinen, die mittels &#039;&#039;INTERRUPT&#039;&#039; deklariert sind, gibt es keinen direkten Ersatz in Form eines Makros. Solche Routinen sind laut Dokumentation der avr-libc in folgender Form zu deklarieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* ** alt ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
INTERRUPT(SIG_OVERFLOW0)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ** neu: ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void TIMER0_OVF_vect(void) __attribute__((interrupt));&lt;br /&gt;
void TIMER0_OVF_vect(void) &lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von &#039;&#039;INTERRUPT&#039;&#039; die Header-Datei &#039;&#039;compat/deprecated.h&#039;&#039; einzubinden. Man sollte bei dieser Gelegenheit jedoch nochmals überprüfen, ob die Funktionalität von &#039;&#039;INTERRUPT&#039;&#039; tatsächlich gewollt ist. In vielen Fällen wurde &#039;&#039;INTERRUPT&#039;&#039; dort genutzt, wo eigentlich &#039;&#039;SIGNAL&#039;&#039; (nunmehr &#039;&#039;ISR&#039;&#039;) hätte genutzt werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;inp&#039;&#039; und &#039;&#039;outp&#039;&#039; zum Einlesen bzw. Schreiben von Registern sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
unsigned char i, j;&lt;br /&gt;
&lt;br /&gt;
// alt:&lt;br /&gt;
  i = inp(PINA);&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  outp(PORTB, j);&lt;br /&gt;
&lt;br /&gt;
// neu (nicht mehr wirklich neu...):&lt;br /&gt;
  i = PINA&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  PORTB = j;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von inp und outp die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Zugriff auf Bits in Registern ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;cbi&#039;&#039; und &#039;&#039;sbi&#039;&#039; zum Löschen und Setzen von Bits sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg. Die Bezeichnung ist ohnehin irreführend da die Funktionen nur für Register mit Adressen im unteren Speicherbereich tatsächlich in die Assembleranweisungen cbi und sbi übersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// alt:&lt;br /&gt;
  sbi(PORTB, PB2);&lt;br /&gt;
  cbi(PORTC, PC1);&lt;br /&gt;
&lt;br /&gt;
// neu (auch nicht mehr wirklich neu...):&lt;br /&gt;
  PORTB |=  (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
  PORTC &amp;amp;= ~(1&amp;lt;&amp;lt;PC1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von sbi und cbi die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden. Wer unbedingt will, kann sich natürlich eigene Makros mit aussagekräftigeren Namen definieren. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &amp;amp;= ~(1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Selbstdefinierte (nicht-standardisierte) ganzzahlige Datentypen ===&lt;br /&gt;
&lt;br /&gt;
Bei den im Folgenden genannten Typdefinitionen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich, bei der Überarbeitung von altem Code die im Abschnitt &#039;&#039;standardisierten ganzzahligen Datentypen&#039;&#039; beschriebenen Datentypen zu nutzen (stdint.h) und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef unsigned char      BYTE;       // besser: uint8_t  aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned short     WORD;       // besser: uint16_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long      DWORD;      // besser: uint32_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long long QWORD;      // besser: uint64_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; BYTE : Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 255. &lt;br /&gt;
&lt;br /&gt;
; WORD : Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 65535. &lt;br /&gt;
&lt;br /&gt;
; DWORD : Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
; QWORD : Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Funktionen im Makefile ==&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken (Libraries/.a-Dateien) hinzufügen ===&lt;br /&gt;
&lt;br /&gt;
Um Funktionen aus Bibliotheken (&amp;quot;echte&amp;quot; Libraries, *.a-Dateien) zu nutzen, sind dem Linker die Namen der Bibliotheken als Parameter zu übergeben. Dazu ist die Option -l (kleines L) vorgesehen, an die der Name der Library angehängt wird. &lt;br /&gt;
&lt;br /&gt;
Dabei ist zu beachten, dass der Name der Library und der Dateiname der Library nicht identisch sind. Der hinter -l angegebene Name entspricht dem Dateinamen der Library ohne die Zeichenfolge &#039;&#039;lib&#039;&#039; am Anfang des Dateinamens und ohne die Endung &#039;&#039;.a&#039;&#039;. Sollen z.B. Funktionen aus einer Library mit dem Dateinamen &#039;&#039;libefsl.a&#039;&#039; eingebunden (gelinkt) werden, lautet der entsprechende Parameter -lefsl (vergl. auch -lm zum Anbinden von libm.a). &lt;br /&gt;
&lt;br /&gt;
In Makefiles wird traditonell eine make-Variable LDLIBS genutzt, in die &amp;quot;l-Parameter&amp;quot; abgelegt werden. Die WinAVR-makefile-Vorlage enthält diese Variable zwar nicht, dies stellt jedoch keine Einschränkung dar, da alle in der make-Variable LDFLAGS abgelegten Parameter an den Linker weitergereicht werden. &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Einbinden von Funktionen aus einer Library efsl (Dateiname libefsl.a)&lt;br /&gt;
LDFLAGS += -lefsl&lt;br /&gt;
# Einbinden von Funktionen aus einer Library xyz (Dateiname libxyz.a)&lt;br /&gt;
LDFLAGS += -lxyz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Liegen die Library-Dateien nicht im Standard Library-Suchpfad, sind die Pfade mittels Parameter &#039;&#039;-L&#039;&#039; ebenfalls anzugeben. (Der vordefinierte Suchpfad kann mittels &#039;&#039;avr-gcc --print-search-dirs&#039;&#039; angezeigt werden.)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Projekt (&amp;quot;superapp2&amp;quot;), in dem der Quellcode von zwei Libraries (efsl und xyz) und der Quellcode der eigentlichen Anwendung in verschiedenen Verzeichnissen mit der folgenden &amp;quot;Baumstruktur&amp;quot; abgelegt sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
superapp2&lt;br /&gt;
|&lt;br /&gt;
+----- efslsource (darin libefsl.a)&lt;br /&gt;
|&lt;br /&gt;
+----- xyzsource (darin libxyz.a)&lt;br /&gt;
|&lt;br /&gt;
+----- firmware (darin Anwendungs-Quellcode und Makefile)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt, dass im Makefile die Verzeichnis efslsource und xyzsource in den Library-Suchpfad aufzunehmen sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
LDFLAGS += -L../efslsource/ -L../xyzsource/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fuse-Bits ===&lt;br /&gt;
&lt;br /&gt;
Zur Berechnung der Fuse-Bits bietet sich neben dem Studium des Datenblattes auch der [http://palmavr.sourceforge.net/cgi-bin/fc.cgi AVR Fuse Calculator] an. Gewarnt werden muss vor der Benutzung von PonyProg, weil dort durch die negierte Darstellung gern Fehler gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Soll die Programmierung von Fuse- und Lockbits automatisiert werden, kann man dies ebenfalls durch Einträge im Makefile vornehmen, die beim Aufruf von &amp;quot;make program&amp;quot; an die genutzte Programmiersoftware übergeben werden. In der makefile-Vorlage von WinAVR (und mfile) gibt es dafuer jedoch keine &amp;quot;Ausfüllhilfe&amp;quot; (Stand 9/2006). Die folgenden Ausführungen gelten für die Programmiersoftware [[AVRDUDE]] (Standard in der WinAVR-Vorlage), können jedoch sinngemäß auf andere Programmiersoftware übertragen werden, die die Angabe der Fuse- und Lockbits-Einstellungen per Kommandozeilenparameter unterstützt (z.B. stk500.exe). Im einfachsten Fall ergänzt man im Makefile einige Variablen, deren Werte natürlich vom verwendeten Controller und den gewünschten Einstellungen abhängen (vgl. Datenblatt Fuse-/Lockbits):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#---------------- Programming Options (avrdude) ----------------&lt;br /&gt;
&lt;br /&gt;
#...&lt;br /&gt;
#Beispiel! f. ATmega16 - nicht einfach uebernehmen! Zahlenwerte anhand&lt;br /&gt;
#--------- des Datenblatts nachvollziehen und gegebenenfalls aendern.&lt;br /&gt;
#&lt;br /&gt;
AVRDUDE_WRITE_LFUSE = -U lfuse:w:0xff:m&lt;br /&gt;
AVRDUDE_WRITE_HFUSE = -U hfuse:w:0xd8:m&lt;br /&gt;
AVRDUDE_WRITE_LOCK  = -U lock:w:0x2f:m&lt;br /&gt;
#...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit diese Variablen auch genutzt werden, ist der Aufruf von avrdude im Makefile entsprechend zu ergänzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Program the device.  &lt;br /&gt;
program: $(TARGET).hex $(TARGET).eep&lt;br /&gt;
# ohne Fuse-/Lock-Einstellungen (nach WinAVR Vorlage Stand 4/2006)&lt;br /&gt;
#	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
#        $(AVRDUDE_WRITE_EEPROM)&lt;br /&gt;
# mit Fuse-/Lock-Einstellungen&lt;br /&gt;
        $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_LFUSE) \&lt;br /&gt;
        $(AVRDUDE_WRITE_HFUSE) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
        $(AVRDUDE_WRITE_EEPROM) $(AVRDUDE_WRITE_LOCK)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weiter Möglichkeit besteht darin, die Fuse- und Lockbit-Einstellungen vom Preprozessor/Compiler generieren zu lassen. Die Fuse-Bits werden dann bei Verwendung von AVRDUDE in eigene Hex-Files geschrieben. Hierzu kann man z.B. folgendes Konstrukt verwenden:&lt;br /&gt;
&lt;br /&gt;
In eine der C-Sourcen wird eine Variable je Fuse-Byte vom Typ &#039;&#039;unsigned char&#039;&#039; deklariert und in eine extra Section gepackt. Dies kann entweder in einem vorhandenen File passieren oder in ein neues (z.B. fuses.c) geschrieben werden. Das File muss im Makefile aber auf jeden Fall mit kompiliert und gelinkt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// tiny 2313 fuses low byte&lt;br /&gt;
#define CKDIV8  7&lt;br /&gt;
#define CKOUT   6&lt;br /&gt;
#define SUT1    5&lt;br /&gt;
#define SUT0    4&lt;br /&gt;
#define CKSEL3  3&lt;br /&gt;
#define CKSEL2  2&lt;br /&gt;
#define CKSEL1  1&lt;br /&gt;
#define CKSEL0  0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses high byte&lt;br /&gt;
#define DWEN       7&lt;br /&gt;
#define EESAVE     6&lt;br /&gt;
#define SPIEN      5&lt;br /&gt;
#define WDTON      4&lt;br /&gt;
#define BODLEVEL2  3&lt;br /&gt;
#define BODLEVEL1  2&lt;br /&gt;
#define BODLEVEL0  1&lt;br /&gt;
#define RSTDISBL   0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses extended byte&lt;br /&gt;
#define SELFPRGEN  0&lt;br /&gt;
&lt;br /&gt;
#define LFUSE         __attribute__ ((section (&amp;quot;lfuses&amp;quot;)))&lt;br /&gt;
#define HFUSE         __attribute__ ((section (&amp;quot;hfuses&amp;quot;)))&lt;br /&gt;
#define EFUSE         __attribute__ ((section (&amp;quot;efuses&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// select ext crystal 3-8Mhz&lt;br /&gt;
unsigned char lfuse LFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;CKDIV8) | (1&amp;lt;&amp;lt;CKOUT) | (1&amp;lt;&amp;lt;CKSEL3) | (1&amp;lt;&amp;lt;CKSEL2) | &lt;br /&gt;
      (0&amp;lt;&amp;lt;CKSEL1) | (1&amp;lt;&amp;lt;CKSEL0) | (0&amp;lt;&amp;lt;SUT1) | (1&amp;lt;&amp;lt;SUT0) );&lt;br /&gt;
unsigned char hfuse HFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;DWEN) | (1&amp;lt;&amp;lt;EESAVE) | (0&amp;lt;&amp;lt;SPIEN) | (1&amp;lt;&amp;lt;WDTON) | &lt;br /&gt;
      (1&amp;lt;&amp;lt;BODLEVEL2) | (1&amp;lt;&amp;lt;BODLEVEL1) | (0&amp;lt;&amp;lt;BODLEVEL0) | (1&amp;lt;&amp;lt;RSTDISBL) );&lt;br /&gt;
unsigned char efuse EFUSE =&lt;br /&gt;
    ((0&amp;lt;&amp;lt;SELFPRGEN));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG: Die Bitpositionen wurden nicht vollständig getestet!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Eine &amp;quot;1&amp;quot; bedeutet hier, dass das Fuse-Bit &#039;&#039;nicht&#039;&#039; programmiert wird - die Funktion also i.A. nicht aktiviert ist. Eine &amp;quot;0&amp;quot; hingegen aktiviert die meisten Funktionen. Dies ist wie im Datenblatt (1 = unprogrammed, 0 = programmed).&lt;br /&gt;
&lt;br /&gt;
Das Makefile muss nun noch um folgende Targets erweitert werden (mit Tabulator einrücken - nicht mit Leerzeichen):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
lfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j lfuses --change-section-address lfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-lfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-lfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U lfuse:w:$(TARGET)-lfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
hfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j hfuses --change-section-address hfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-hfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-hfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U hfuse:w:$(TARGET)-hfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
efuses: build&lt;br /&gt;
        -$(OBJCOPY) -j efuses --change-section-address efuses=0 \&lt;br /&gt;
         -O ihex $(TARGET).elf $(TARGET)-efuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-efuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U efuse:w:$(TARGET)-efuse.hex;&lt;br /&gt;
        fi;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Target &amp;quot;clean&amp;quot; muss noch um die Zeilen&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
        $(REMOVE) $(TARGET)-lfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-hfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-efuse.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
erweitert werden, wenn auch die Fuse-Dateien gelöscht werden sollen.&lt;br /&gt;
&lt;br /&gt;
Um nun die Fusebits des angeschlossenen Controllers zu programmieren muss lediglichein &amp;quot;make lfuses&amp;quot;, &amp;quot;make hfuses&amp;quot; oder &amp;quot;make efuses&amp;quot; gestartet werden.&lt;br /&gt;
Bei den Fuse-Bits ist besondere Vorsicht geboten, da diese das Programmieren des Controllers unmöglich machen können. Also erst programmieren, wenn man einen HV-Programmierer hat oder ein paar Reserve-AVRs zur Hand ;-)&lt;br /&gt;
&lt;br /&gt;
Um weiterhin den &amp;quot;normalen&amp;quot; Flash beschreiben zu können, ist es wichtig, für das Target &amp;quot;*.hex&amp;quot; im Makefile nicht nur &amp;quot;-R .eeprom&amp;quot; als Parameter zu übergeben sondern zusätzlich noch &amp;quot;-R lfuses -R efuses -R hfuses&amp;quot;. Sonst bekommt AVRDUDE Probleme diese Sections in den Flash (wo sie ja nicht hingehören) zu schreiben.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[AVR_Fuses#Vergleich_der_Fuses_bei_verschiedenen_Programmen|Vergleich der Fuses bei verschiedenen Programmen]]&lt;br /&gt;
&lt;br /&gt;
== Externe Referenzspannung des internen Analog-Digital-Wandlers ==&lt;br /&gt;
&lt;br /&gt;
Die minimale (externe) Referenzspannung des ADC darf nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. z.B. beim ATMEGA8 darf sie laut Datenblatt (S.245, Tabelle 103, Zeile &amp;quot;VREF&amp;quot;) 2,0V nicht unterschreiten. HINWEIS: diese Information findet sich erst in der letzten Revision (Rev. 2486O-10/04) des Datenblatts.&lt;br /&gt;
&lt;br /&gt;
Meiner &amp;lt;!-- Wer? - es gibt inzwischen x Leute die mehr oder weniger viel in diesem Artikel geschrieben haben --&amp;gt; eigenen Erfahrung nach kann man aber (auf eigene Gefahr und natürlich nicht für Seriengeräte) durchaus noch ein klein wenig weiter heruntergehen, bei dem von mir unter die Lupe genommenen ATMEGA8L (also die Low-Voltage-Variante) funktioniert der ADC bei 5V Betriebsspannung mit bis zu VREF=1,15V hinunter korrekt, ab 1,1V und darunter digitalisiert er jedoch nur noch Blödsinn). Ich würde sicherheitshalber nicht unter 1,5V gehen und bei niedrigeren Betriebsspannungen mag sich die Untergrenze für VREF am Pin AREF ggf. nach oben&#039;&#039;&#039;(!)&#039;&#039;&#039; verschieben.&lt;br /&gt;
&lt;br /&gt;
In der letzten Revision des Datenblatts ist außerdem korrigiert, dass ADC4 und ADC5 sehr wohl 10 Bit Genauigkeit bieten (und nicht bloß 8 Bit, wie in älteren Revisionen irrtümlich angegeben.)&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* stdio.h, malloc() &lt;br /&gt;
* Code-Optimierungen (&amp;quot;tricks&amp;quot;), siehe auch Application Note [http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf AVR035: Efficient C Coding for AVR]&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
* SPI siehe [http://www.uni-koblenz.de/~physik/informatik/MCU/SPI.pdf SPI Bus mit Atmel AVR]&lt;br /&gt;
* I²C / TWI Bus [http://www.roboternetz.de/wissen/index.php/TWI]&lt;br /&gt;
* Bootloader (bez. auf boot.h)&lt;br /&gt;
* CAN-Bus&lt;br /&gt;
* Einsatz von einfachen Betriebssystemen auf dem AVR&lt;br /&gt;
* Übersicht zu den C bzw. GCC-predefined Makros (__DATE__, __TIME__,...)&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
* ADC ; &lt;br /&gt;
* Timer&lt;br /&gt;
* USB ; Steuerung mit USB&lt;br /&gt;
* Multiplexen Siebensegment&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28251</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28251"/>
		<updated>2008-05-27T16:38:44Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Einführungsbeispiel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[AVR-GCC]] erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Progammiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong]. Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern, weder in Assembler noch in einer anderen Sprache. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR Controllern finden. Bei WinAVR gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden stetig weiterentwickelt. Erläuterungen und Beispiele beziehen sich auf den C-Compiler avr-gcc ab Version 3.4 und die avr-libc ab Version 1.4.3. Die Unterschiede zu älteren Versionen werden im Haupttext und Anhang zwar erläutert, Anfängern sei jedoch empfohlen, die aktuellen Versionen zu nutzen (für MS-Windows: aktuelle Version des [[WinAVR]]-Pakets). &lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in PDF-Form erhältlich bei:&lt;br /&gt;
http://www.siwawi.arubi.uni-kl.de/avr_projects/AVR-GCC-Tutorial_-_www_mikrocontroller_net.pdf&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels avr-gcc/avr-libc zu erstellen und zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR Controllers, der vom avr-gcc Compiler unterstützt wird (alle ATmegas und die meisten AT90, siehe Dokumentation der avr-libc für unterstützte Typen). Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man [[AVR#Starterkits|hier]].&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc Compiler und die avr-libc. Kostenlos erhältlich für nahezu alle Plattformen und Betriebssysteme. Für MS-Windows im Paket [[WinAVR]]; für Unix/Linux siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]]).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder nur die eigenen C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
&lt;br /&gt;
* Die [[AVR Checkliste]] durcharbeiten.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/2 GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  auf den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
&lt;br /&gt;
* Die Beispieleprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweisen sind aber auch auf C-Programme übertragbar.&lt;br /&gt;
&lt;br /&gt;
* Einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien, um das Problem nachzuvollziehen, sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z. B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.&amp;amp;nbsp;B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.&amp;amp;nbsp;B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen, um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu zwei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer integrierten Entwicklungsumgebung (IDE = &#039;&#039;&#039;I&#039;&#039;&#039;ntegrated &#039;&#039;&#039;D&#039;&#039;&#039;evelopment &#039;&#039;&#039;E&#039;&#039;&#039;nvironment), bei der alle Einstellungen z.&amp;amp;nbsp;B. in Dialogboxen durchgeführt werden können. Unter Anderem kann AVRStudio ab Version 4.12 (kostenlos auf [http://www.atmel.com/ atmel.com]) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs für den avr-gcc (ohne Anspruch auf Vollständigkeit): AtmanAvr C (relativ günstig), KamAVR (kostenlos), VMLab (ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.&amp;amp;nbsp;B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand der universellen und plattformunabhängigen Vorgehensweise mittels make und Makefiles näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind lediglich Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xff;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5a)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/;  // (5b)&lt;br /&gt;
   }                         // (5c)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (6)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In der mit (1) markierten Zeile wird eine so genannte Header-Datei eingebunden. In io.h sind die Registernamen definiert, die im späteren Verlauf genutzt werden.&lt;br /&gt;
&lt;br /&gt;
* Bei (2) beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion main.&lt;br /&gt;
&lt;br /&gt;
* Die Anschlüsse eines AVR (&amp;quot;Beinchen&amp;quot;) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Richtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B zu Ausgängen.&lt;br /&gt;
&lt;br /&gt;
* (4) stellt die Werte der Ausgänge ein. Die den ersten beiden Bits des Ports zugeordneten Anschlüsse (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential).&lt;br /&gt;
&lt;br /&gt;
* (5) ist die so genannte Hauptschleife (main-loop). Dies ist eine Programmschleife, welche kontinuierlich wiederkehrende Befehle enthält. In diesem Beispiel ist sie leer. Der Controller durchläuft die Schleife immer wieder, ohne dass etwas passiert (außer das Strom verbraucht wird). Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Ohne diese Schleife wäre der Zustand des Controllers nach dem Programmende undefiniert.&lt;br /&gt;
&lt;br /&gt;
* (6) wäre das Programmende. Die Zeile ist nur aus Gründen der C-Kompatibilität enthalten: int main(void) besagt, dass die Funktion einen Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein auf dem Controller lauffähiges Programm zu übersetzen, wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen Makefile (ohne Endung) im selben Verzeichnis, in dem auch die Datei main.c mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im folgenden Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\tmp\gcc_tut\quickstart&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei main.hex, in der der Code für den AVR enthalten ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.&amp;amp;nbsp;B. über In-System-Programming (ISP) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann (z.&amp;amp;nbsp;B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio), kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine LED leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode mit einem dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Exkurs: Makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Plattformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.com) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das Makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den im Makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im Makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten Makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt [[AVR-GCC-Tutorial#EEPROM|Speicherzugriffe]]), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendeten Controllers gesetzt. Eine Liste der von avr-gcc und der avr-libc unterstützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien eintragen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
# nicht auskommentiert: EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Das erstellte Makefile und der Code müssen im gleichen Ordner sein, auch sollte der Dateiname nicht verändert werden. &lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem Makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschinencode erzeugt. Beim Update auf eine neue Compilerversion ist zu beachten, dass diese möglicherweise intern andere Optimierungsalgorithmen verwenden und sich dadurch die Größe des Machinencodes etwas ändert, ohne dass man im Quellcode etwas geändert hat.&lt;br /&gt;
&lt;br /&gt;
Als Orientierungswerte die Größe des Maschinencodes bei verschiedenen Optionen für einen nicht näher spezifizierten relativ kleinen Testcode bei Verwendung einer nicht näher spezifizierten Compilerversion. &lt;br /&gt;
&lt;br /&gt;
* -O0 : 12&#039;217 Byte&lt;br /&gt;
* -O1 : 9&#039;128 Byte&lt;br /&gt;
* -O2 : 1&#039;670 Byte&lt;br /&gt;
* -O3 : 3&#039;004 Byte&lt;br /&gt;
* -Os : 1&#039;695 Byte&lt;br /&gt;
&lt;br /&gt;
Im diesem Testfall führt die Option -O2 mit zum kompaktesten Code, dies  allerdings hier nur mit 25 Bytes &amp;quot;Vorsprung&amp;quot;. Es kann durchaus sein, dass nur wenige Programmerweiterungen dazu führen, dass Compilieren mit -Os wieder in kompakteren Code resultiert.&lt;br /&gt;
&lt;br /&gt;
siehe dazu auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/using_tools.html#gcc_optO avr-libc manual Abschnitt Using the gnu-tools/Compiler-Optionen]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_optflags avr-libc Manual FAQ Nr. 16] (Stand avr-libc Version 1.4.5)&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wird hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (großes S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Taktfrequenz ===&lt;br /&gt;
&lt;br /&gt;
Neuere Versionen der WinAVR/Mfile Vorlage für Makefiles beinhalten die Definition einer Variablen F_CPU (WinAVR 2/2005). Darin wird die Taktfrequenz des Controllers in Hertz eingetragen. Die Definition steht dann im gesamten Projekt ebenfalls unter der Bezeichnung F_CPU zur Verfügung (z.B. um daraus  UART-, SPI- oder ADC-Frequenzeinstellungen abzuleiten).&lt;br /&gt;
&lt;br /&gt;
Die Angabe hat rein &amp;quot;informativen&amp;quot; Charakter, die tatsächliche Taktrate wird über den externen Takt (z.B. Quarz) bzw. die Einstellung des internen R/C-Oszillators  bestimmt. Die Nutzung von F_CPU hat also nur Sinn, wenn die Angabe mit dem tatsächlichen Takt übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Innerhalb neuerer Versionen der avr-libc (ab Version 1.2) wird die Definition der Taktfrequenz (F_CPU) zur Berechnung der Wartefunktionen in delay.h genutzt. Diese funktionieren nur dann korrekt, wenn F_CPU mit der tatsächlichen Taktfrequenz übereinstimmt.&lt;br /&gt;
F_CPU muss dazu jedoch nicht unbedingt im makefile definiert werden. Es reicht aus, wird aber bei mehrfacher Anwendung unübersichtlich, vor &#039;&#039;#include &amp;lt;util/delay.h&amp;gt;&#039;&#039; (veraltet: &#039;&#039;#include &amp;lt;avr/delay.h&amp;gt;&#039;&#039;) ein &#039;&#039;#define F_CPU [hier Takt in Hz]UL&#039;&#039; einzufügen. Bei Nutzung von delay.h ist darauf zu achten, dass die Optimierung des Compilers nicht ausgeschaltet ist, sonst wird sehr viel Code erzeugt und die Wartezeit stimmt nicht mit der gewünschten Zeitspanne überein.&lt;br /&gt;
Vgl. dazu  den [http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html entsprechenden Abschnitt der Dokumentation].&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff: (sollte nur noch in Ausnahmefällen genutzt werden)&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
Die Verwendung von Makefiles bietet noch viele weiter Möglichkeiten, einige davon werden im Anhang [[AVR-GCC-Tutorial#Zus.C3.A4tzliche_Funktionen_im_Makefile|Zusätzliche Funktionen im Makfile]] erläutert.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige (Integer) Datentypen =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei stdint.h definiert. &lt;br /&gt;
Zur Nutzung der standardisierten Typen bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// ab avr-libc Version 1.2.0 möglich und empfohlen:&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// veraltet: #include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef signed char        int8_t;&lt;br /&gt;
typedef unsigned char      uint8_t;&lt;br /&gt;
&lt;br /&gt;
typedef short              int16_t;&lt;br /&gt;
typedef unsigned short     uint16_t;&lt;br /&gt;
&lt;br /&gt;
typedef long               int32_t;&lt;br /&gt;
typedef unsigned long      uint32_t;&lt;br /&gt;
&lt;br /&gt;
typedef long long          int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* int8_t steht für einen 8-Bit Integer mit einem Wertebereich -128 bis +127.&lt;br /&gt;
* uint8_t steht für einen 8-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 255&lt;br /&gt;
&lt;br /&gt;
* int16_t steht für einen 16-Bit Integer mit einem Wertebereich -32768 bis +32767.&lt;br /&gt;
* uint16_t steht für einen 16-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 65535.&lt;br /&gt;
&lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; werden vorzeichenbehaftete Zahlen abgespeichert. Typen mit vorgestelltem &#039;&#039;u&#039;&#039; dienen der Ablage von postiven Zahlen (inkl. 0).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/(Standard) Integer Types&lt;br /&gt;
&lt;br /&gt;
= Bitfelder =&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&lt;br /&gt;
&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in eine einzelne Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitfelder sparen Platz im RAM, zu Lasten von Platz im Flash, verschlechtern aber unter Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.&lt;br /&gt;
&lt;br /&gt;
Wenn man nur eine paar wenige Variablen vom Typ bool verwenden möchte, kann man&lt;br /&gt;
auch die Headerdatei &amp;lt;stdbool.h&amp;gt; einbinden und sich dann wie gewohnt einen Booltyp anlegen. Variablen dieses Typs brauchen dennoch 1 Byte Speicher, ermöglichen aber eine genaue Unterscheidung zwischen Zahlenvariable und boolscher Variable.&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;C-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum können für allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden&lt;br /&gt;
AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039;, wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output).  Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt.&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend&lt;br /&gt;
angepasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O-Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek (avr-libc) stellt auch Funktionen zur Abfrage eines einzelnen Bits eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist, wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind &#039;&#039;nicht erforderlich&#039;&#039;, man kann auch &amp;quot;einfache&amp;quot; C-Syntax verwenden, die universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) wenn das Bit gesetzt und 0 (&amp;quot;falsch&amp;quot;) wenn es nicht gesetzt ist. Die Negierung des Ausdrucks, also  &#039;&#039;&#039;!&#039;&#039;&#039;(Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)), entspricht &#039;&#039;bit_is_clear&#039;&#039; und gibt einen Wert &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) zurück, falls das Bit nicht gesetzt ist,&lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O-Register einfach wie eine Variable setzen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und outp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben von Bits ====&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausdruck:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit (für 1) eines Bytes hat die Bitnummer 0, das &amp;quot;höchstwertige&amp;quot; (für 128) die Nummer 7.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );    /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3);   /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die warten, bis ein bestimmter Zustand auf einem Bit erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend gewartet&amp;quot; wird. D.h., der Programmablauf bleibt an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den Watchdog ein, muss man darauf achten, dass dieser auch noch getriggert wird (zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Änderung: Habe PINx und PORTx vertauscht.PINx ist für Pull up und PORTx für high bzw low. Michael.L  - &lt;br /&gt;
&lt;br /&gt;
Was sollte diese Aenderung?? Den Rest des Abschnitts auch gelesen? Evtl. die hier nicht erlaeuterten Zusatzfunktion der neuen AVRs durcheinandergeworfen? Aenderung verworfen bis mehr Details.  m.thomas. &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
// Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
&lt;br /&gt;
DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
/* mehr Tipparbeit aber übersichtlicher: */&lt;br /&gt;
DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&lt;br /&gt;
/* Einige Compiler erlauben die Eingabe von Konstanten im &lt;br /&gt;
   Binärformat, z.B. gcc in WinAVR, NICHT aber aus&lt;br /&gt;
   GNU-gcc-Quellen selbst erstellte Compiler. Diese Schreib-&lt;br /&gt;
   weise also nicht nutzen, wenn Code mit anderen &lt;br /&gt;
   ausgetauscht werden soll. */&lt;br /&gt;
DDRB = 0b00011111;    /* direkte Zuweisung &lt;br /&gt;
                         - übersichtlicher &lt;br /&gt;
                         - nicht standardkonform &lt;br /&gt;
                         - selten portabel&lt;br /&gt;
                         - nur bei modifiziertem GCC */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
Weitere Beispiele:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
DDRB = 0xff; &lt;br /&gt;
// Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; DDB0 );&lt;br /&gt;
// Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( ( 1 &amp;lt;&amp;lt; DDB3 ) | ( 1&amp;lt;&amp;lt;DDB4) );&lt;br /&gt;
// Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB |= ( 1 &amp;lt;&amp;lt; DDB0) | ( 1 &amp;lt;&amp;lt; DDB3 );&lt;br /&gt;
// Alle Pins auf Eingang:&lt;br /&gt;
DDRB = 0x00;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&#039;&#039;&lt;br /&gt;
* &#039;&#039;zuerst die Bits im PORTx-Register setzen und&#039;&#039;&lt;br /&gt;
* &#039;&#039;anschließend die Datenrichtung auf Ausgang stellen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Daraus ergibt sich die Abfolge für einen Eingangspin:&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze PORTx: interner Pull-Up aktiv&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze DDRx: Ausgang &amp;quot;high&amp;quot;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bei der Reihenfolge erst DDRx und dann PORTx, kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;&#039;nicht&#039;&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, &#039;&#039;&#039;sondern&#039;&#039;&#039; Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.&amp;lt;/font&amp;gt; (Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände.)&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch.  Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVRs haben sogar an den meisten Pins softwaremäßig zuschaltbare interne Pull-Up Widerstände, welche wir natürlich auch verwenden können.&lt;br /&gt;
&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;DDC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== (Tasten-)Entprellung ====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim Schließen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&lt;br /&gt;
&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen als mehrfache Impulse gezählt wird. Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* bei alter avr-libc: #include &amp;lt;avr/delay.h&amp;gt; */      &lt;br /&gt;
&lt;br /&gt;
/* Einfache Funktion zum Entprellen eines Tasters */&lt;br /&gt;
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)&lt;br /&gt;
{&lt;br /&gt;
    if ( ! (*port &amp;amp; (1 &amp;lt;&amp;lt; pin)) )&lt;br /&gt;
    {&lt;br /&gt;
        /* Pin wurde auf Masse gezogen, 100ms warten   */&lt;br /&gt;
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz&lt;br /&gt;
        _delay_ms(50); &lt;br /&gt;
        if ( *port &amp;amp; (1 &amp;lt;&amp;lt; pin) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Anwender Zeit zum Loslassen des Tasters geben */&lt;br /&gt;
            _delay_ms(50);&lt;br /&gt;
            _delay_ms(50); &lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; PB0 );                 /* PIN PB0 auf Eingang (Taster)            */&lt;br /&gt;
    PORTB |= ( 1 &amp;lt;&amp;lt; PB0 );                 /* Pullup-Widerstand aktivieren            */&lt;br /&gt;
    ...&lt;br /&gt;
    if (debounce(&amp;amp;PINB, PB0))             /* Falls Taster an PIN PB0 gedrueckt..    */&lt;br /&gt;
        PORTD = PIND ^ ( 1 &amp;lt;&amp;lt; PD7 );  /* ..LED an Port PD7 an-&lt;br /&gt;
                                   bzw. ausschalten */&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesem Beispiel ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritische Anwendungen sollte man ein anderes Verfahren nutzen (z.B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).&lt;br /&gt;
&lt;br /&gt;
Zum Thema Entprellen siehe auch:&lt;br /&gt;
* Artikel [[Entprellung]]&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls ben&amp;amp;ouml;tigt, kann eine 16-Bit Variable auch recht einfach manuell in ihre zwei 8-Bit Bestandteile zerlegt werden. Folgendes Beispiel demonstriert dies anhand des pseudo- 16-Bit Registers UBRR.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
typedef union {&lt;br /&gt;
        uint16_t i16;&lt;br /&gt;
        struct {&lt;br /&gt;
                uint8_t i8l;&lt;br /&gt;
                uint8_t i8h;&lt;br /&gt;
        };&lt;br /&gt;
} convert16to8;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
convert16to8 baud;&lt;br /&gt;
baud.i16 = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
UBRRH = baud.i8h;&lt;br /&gt;
UBRRL = baud.i8l;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/*alternativ:*/&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint16_t wFoo16;&lt;br /&gt;
uint8_t bFooLow, bFooHigh;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
wFoo16   = 0xAA55;                 /* zu &amp;quot;zerlegende&amp;quot; 16Bit-Integer */&lt;br /&gt;
bFooHigh = (uint8_t)(wFoo16 &amp;gt;&amp;gt; 8); /* MS-Byte */&lt;br /&gt;
bFooLow  = (uint8_t)(wFoo16);      /* LS-Byte */&lt;br /&gt;
...&lt;br /&gt;
#define us0(Data) ((unsigned char *)(&amp;amp;Data))&lt;br /&gt;
#define us1(Data) ((unsigned char *)((&amp;amp;Data)+1))&lt;br /&gt;
bFooHigh = us1(wFoo16);&lt;br /&gt;
bFoolow  = us0(wFoo16);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen (z.B. ATmega8) teilen sich UBRRH und UCSRC die gleiche Memory-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL) welches Register tats&amp;amp;auml;chlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und &amp;amp;uuml;bergibt den Wert &#039;&#039;3&#039;&#039; und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und &amp;amp;uuml;bergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Bei einigen 16-bit Registern (insbesondere die der 16-bit Timer) erfolgen Schreibzugriffe über das sogenannte &#039;&#039;temporary Register&#039;&#039;. Die Reihenfolge der Zugriffe bestimmt, wann der Wert tatsächlich ins Register geschrieben wird. Typisch wird erst das High-Byte beschrieben (xxxH) und intern im temporary Register zwischengespeichert. Nachdem das Low-Byte (xxxL) geschrieben wurde, setzt der Controller mit diesem und dem im temporary Register zwischengespeicherten Wert für das High-Byte das 16-bit Register. Dabei ist zu beachten, dass intern nur ein temporary Register verfügbar ist, welches in Interruptroutinen mglw. mit einem anderen Wert überschrieben wird, wenn dort ebenfalls 16-bit Register beschrieben werden. avr-gcc/avr-libc berücksichtigen die korrekte Reihenfolge automatisch, wenn die Register mit ihrem &amp;quot;16-bit Label&amp;quot; (ohne H bzw. L) angesprochen werden, dabei ist der Schutz des temporary Registers vor Überschreiben durch Interruptroutinen dennoch zu beachten (im Zweifel beim Schreibzugriff die Interrupts kurzzeitig global deaktivieren).&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t key_pressed(const volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if ( last_state == ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) ) ) {&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    i = key_pressed( &amp;amp;PINB, PB1 );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Im Übrigen können mit volatile gekennzeichnete Variablen auch als const deklariert werden, um sicherzustellen, dass sie nur noch von der Hardware änderbar sind.&lt;br /&gt;
&lt;br /&gt;
= Der UART =&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Über den [[UART]] kann ein AVR leicht mit einer [[RS-232]]-Schnittstelle eines PC oder sonstiger Geräte mit &amp;quot;serieller Schnittstelle&amp;quot; verbunden werden. &lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungen des UART:&lt;br /&gt;
&lt;br /&gt;
* Debug-Schnittstelle: z.B. zur Anzeige von Zwischenergebnissen (&amp;quot;printf-debugging&amp;quot; - hier besser &amp;quot;UART-debugging&amp;quot;) auf einem PC. Auf dem Rechner reicht dazu ein [[RS-232#Terminalprogramme|Terminalprogramm]] (MS-Windows: Hyperterm oder besser Bray-Terminal, [http://www.mikrocontroller.net/forum/read-8-155472.html#new HTerm]; Unix/Linux z.B. minicom). Ein direkter Anschluss ist aufgrund unterschiedlicher Pegel nicht möglich, jedoch sind entsprechende Schnittstellen-ICs wie z.B. ein MAX232 günstig und leicht zu integrieren. Rechner ohne serielle Schnittstelle können über fertige USB-seriell-Adapter angeschlossen werden. &lt;br /&gt;
* &amp;quot;Mensch-Maschine Schnittstelle&amp;quot;: z.B. Konfiguration und Statusabfrage über eine &amp;quot;Kommandozeile&amp;quot; oder Menüs (siehe z.B. Forumsbeitrag [http://www.mikrocontroller.net/topic/52985 Auswertung RS232-Befehle]) &lt;br /&gt;
* Übertragen von gespeicherten Werten: z.B. bei einem Datenlogger&lt;br /&gt;
* Anschluss von Geräten mit serieller Schnittstelle (z.B. (Funk-)Modems, Mobiltelefone, Drucker, Sensoren, &amp;quot;intelligente&amp;quot; LC-Displays, GPS-Empfänger). &lt;br /&gt;
* &amp;quot;Feldbusse&amp;quot; auf RS485/RS422-Basis mittels entsprechenden Bustreiberbausteinen (z.B. MAX485)&lt;br /&gt;
* DMX, Midi etc.&lt;br /&gt;
* LIN-Bus (&#039;&#039;&#039;L&#039;&#039;&#039;ocal &#039;&#039;&#039;I&#039;&#039;&#039;nterconnect &#039;&#039;&#039;N&#039;&#039;&#039;etwork): Preiswerte Sensoren/Aktoren in der Automobiltechnik und darüber hinaus&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben ein bis zwei vollduplexfähigen UART (&#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) schon eingebaut (&amp;quot;Hardware-UART&amp;quot;). &lt;br /&gt;
Übrigens: Vollduplex heißt nichts anderes, als dass der Baustein gleichzeitig senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega, ATtiny) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterscheidet sich vom UART hauptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurationsregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit zeigt an, ob der Sendepuffer bereit ist, um ein zu sendendes Zeichen aufzunehmen. Das Bit wird vom AVR gesetzt (1), wenn der Sendepuffer leer ist. Es wird gelöscht (0), wenn ein Zeichen im Sendedatenregister vorhanden ist und noch nicht in das Sendeschieberegister übernommen wurde. Atmel empfiehlt aus Kompatibilitätsgründen mit kommenden µC, UDRE auf 0 zu setzen, wenn das UCSRA Register beschrieben wird.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
\mathrm{UBRR} = \frac{\mathrm{Taktfrequenz}}{\mathrm{Baudrate} \cdot 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS-232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. &lt;br /&gt;
&amp;lt;!-- &amp;quot;Hackerloesung&amp;quot; auskommentiert - nicht so gut in einem &amp;quot;Einsteiger-Tutorial&amp;quot; - mthomas&lt;br /&gt;
Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfiehlt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== UART initialisieren ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben. Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach gewünschter Funktionsweise die benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Da wir vorerst nur senden möchten und noch keine Interrupts auswerten wollen, gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs mit USART haben mehrere Konfigurationsregister und erfordern eine etwas andere Konfiguration. Für einen ATmega16 z.B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                 // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);     // Asynchron 8N1&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist noch das Baudratenregister &#039;&#039;&#039;UBRR&#039;&#039;&#039; einzustellen. Bei neueren AVRs besteht es aus zwei Registern &#039;&#039;&#039;UBRRL&#039;&#039;&#039; und &#039;&#039;&#039;UBRRH&#039;&#039;&#039;. Der Wert dafür ergibt sich aus der angegebenen Formel durch Einsetzen der Taktfrequenz und der gewünschten Übertragungsrate. Das Berechnen der Formel wird dem [[C-Präprozessor|Präprozessor]] überlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann&lt;br /&gt;
   F_CPU im Makefile definiert werden, eine nochmalige Definition&lt;br /&gt;
   hier wuerde zu einer Compilerwarnung fuehren. Daher &amp;quot;Schutz&amp;quot; durch&lt;br /&gt;
   #ifndef/#endif &lt;br /&gt;
&lt;br /&gt;
   Dieser &amp;quot;Schutz&amp;quot; kann zu Debugsessions führen, wenn AVRStudio &lt;br /&gt;
   verwendet wird und dort eine andere, nicht zur Hardware passende &lt;br /&gt;
   Taktrate eingestellt ist: Dann wird die folgende Definition &lt;br /&gt;
   nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) &lt;br /&gt;
   von AVRStudio - daher Ausgabe einer Warnung falls F_CPU&lt;br /&gt;
   noch nicht definiert: */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000&amp;quot;&lt;br /&gt;
#define F_CPU 4000000UL    // Systemtakt in Hz &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600UL          // Baudrate&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) // Fehler in Promille, 1000 = kein Fehler.&lt;br /&gt;
&lt;br /&gt;
#if ((BAUD_ERROR&amp;lt;990) || (BAUD_ERROR&amp;gt;1010))&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;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    UBRR = UBRR_VAL;&lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&amp;lt;!-- mthomas: warum nicht UL?, wird von AVRStudio auch mit UL übergeben --&amp;gt;&lt;br /&gt;
Wieder für den Mega16 mit zwei Registern für die Baudrateneinstellung eine etwas andere Programmierung. Wichtig ist, dass UBRRH &#039;&#039;&#039;vor&#039;&#039;&#039; UBRRL geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmega16 */&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einige AVR (z.B. ATmega169, ATmega48/88/168, AT90CAN jedoch nicht für z.B. ATmega16/32, ATmega128, ATtiny2313) wird durch die Registerdefinitionen der avr-libc (io*.h) auch für Controller mit zwei UBRR-Registern (UBRRL/UBRRH) ein UBRR bzw. UBRR0 als &amp;quot;16-bit-Register&amp;quot; definiert und man kann auch Werte direkt per UBRR = UBRR_VAL zuweisen. Intern werden dann zwei Zuweisungen für UBRRH und UBRRL generiert. Dies ist nicht bei allen Controllern möglich, da die beiden Register nicht bei allen aufeinanderfolgende Addressen aufweisen. Die getrennte Zuweisung an UBRRH und UBRRL wie im Beispiel gezeigt ist universeller und portabler und daher vorzuziehen.&lt;br /&gt;
&lt;br /&gt;
Die Makros sind sehr praktisch, da sie sowohl automatisch den Wert für UBRR als auch den Fehler in der generierten Baudrate berechnen und im Falle einer Überschreitung (+/-1%) einen Fehler und somit Abbruch im Compilerablauf generieren. Damit können viele Probleme mit &amp;quot;UART sendet komische Zeichen&amp;quot; vermieden werden. Ausserdem kann man mühelos die Einstellung an eine neue Taktfrequenz bzw. Baudrate anpassen, ohne selber rechnen oder in Tabellen nachschlagen zu müssen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. Vorher ist zu prüfen, ob das UART-Modul bereit ist, das zu sendende Zeichen entgegenzunehmen. Die Bezeichnungen des/der Statusregisters mit dem Bit UDRE ist abhängig vom Controllertypen (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    // bei AVR mit einem UART (&amp;quot;classic AVR&amp;quot; z.B. AT90S8515)&lt;br /&gt;
    while (!(USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                  /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&lt;br /&gt;
    /** ODER **/&lt;br /&gt;
&lt;br /&gt;
    // bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                    /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe &amp;quot;String senden&amp;quot; wird durch zwei Funktionen abgearbeitet. Die universelle/controllerunabhängige Funktion uart_puts übergibt jeweils ein Zeichen der Zeichenkette an eine Funktion uart_putc, die abhängig von der vorhandenen Hardware implementiert werden muss. In der Funktion zum Senden eines Zeichens ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// putc fuer AVR mit einem UART (z.B. AT90S8515)&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while(!(USR &amp;amp; (1 &amp;lt;&amp;lt; UDRE)))  /* warte, bis UDR bereit */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = c;                     /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** ODER **/&lt;br /&gt;
&lt;br /&gt;
// bei neueren AVRs andere Bezeichnung fuer die Statusregister, hier ATmega16:&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich */&lt;br /&gt;
    {&lt;br /&gt;
    }                             &lt;br /&gt;
&lt;br /&gt;
    UDR = c;                      /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* puts ist unabhaengig vom Controllertyp */&lt;br /&gt;
void uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != &#039;\0&#039; also ungleich dem &amp;quot;String-Endezeichen&amp;quot; */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die in uart_putc verwendeten Schleifen, in denen gewartet wird bis die UART-Hardware zum senden bereit ist, sind insofern etwas kritisch, da während des Sendens eines Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. empfangenen Zeichen/Bytes zwischengespeichert und in Interruptroutinen an die U(S)ART-Hardware weitergegeben bzw. von ihr ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&lt;br /&gt;
&lt;br /&gt;
=== Schreiben von Variableninhalten ===&lt;br /&gt;
&lt;br /&gt;
Sollen Inhalte von Variablen (Ganzzahlen, Fließkomma) in &amp;quot;menschenlesbarer&amp;quot; Form gesendet werden, ist vor dem Transfer eine Umwandlung in Zeichen (&amp;quot;ASCII&amp;quot;) erforderlich. Bei nur einer Ziffer ist diese Umwandlung relativ einfach: man addiert den ASCII-Wert von Null zur Ziffer und kann diesen Wert direkt senden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_putc (s.o.)&lt;br /&gt;
&lt;br /&gt;
// Ausgabe von 0123456789&lt;br /&gt;
char c;&lt;br /&gt;
&lt;br /&gt;
for (uint8_t i=0; i&amp;lt;=9; ++i) {&lt;br /&gt;
    c = i + &#039;0&#039;;&lt;br /&gt;
    uart_putc( c );&lt;br /&gt;
    // verkuerzt: uart_putc( i + &#039;0&#039; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll mehr als eine Ziffer ausgegeben werden, bedient man sich zweckmäßigerweise vorhandener Funktionen zur Umwandlung von Zahlen in Zeichenketten/Strings. Die Funktion der avr-libc zur Umwandlung von vorzeichenbehafteten 16bit-Ganzzahlen (int16_t) in Zeichenketten heißt &#039;&#039;itoa&#039;&#039; (Integer to ASCII). Man muss der Funktion einen Speicherbereich zur Verarbeitung (buffer) mit Platz für alle Ziffern, das String-Endezeichen (&#039;\0&#039;) und evtl. das Vorzeichen bereitstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   char s[7];&lt;br /&gt;
   int16_t i = -12345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   itoa( i, s, 10 ); // 10 fuer radix -&amp;gt; Dezimalsystem&lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // da itoa einen Zeiger auf den Beginn von s zurueckgibt verkuerzt auch:&lt;br /&gt;
   uart_puts( itoa( i, s, 10 ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für vorzeichenlose 16bit-Ganzzahlen (uint16_t) exisitert &#039;&#039;utoa&#039;&#039;. Die Funktionen für 32bit-Ganzzahlen (int32_t und uint32_t) heißen &#039;&#039;ltoa&#039;&#039; bzw. &#039;&#039;ultoa&#039;&#039;. Da 32bit-Ganzzahlen mehr Stellen aufweisen können, ist ein entsprechend größerer Pufferspeicher vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Auch Fließkommazahlen (float/double) können mit breits vorhandenen Funktionen in Zeichenfolgen umgewandelt werden, dazu existieren die Funktionen &#039;&#039;dtostre&#039;&#039; und &#039;&#039;dtostrf&#039;&#039;. dtostre nutzt Exponentialschreibweise (&amp;quot;engineering&amp;quot;-Format). (Hinweis: z.Zt. existiert im avr-gcc kein &amp;quot;echtes&amp;quot; double, intern wird immer mit &amp;quot;einfacher Genauigkeit&amp;quot;, entsprechend float, gerechnet.) &lt;br /&gt;
&lt;br /&gt;
dtostrf und dtostre benötigen die libm.a der avr-libc. Bei Nutzung von Makefiles ist der Parameter -lm in in LDFLAGS anzugeben (Standard in den WinAVR/mfile-Makefilevorlagen). Nutzt man AVRStudio als IDE für den GNU-Compiler (gcc-Plugin) ist die libm.a unter Libaries auszuwählen: Project -&amp;gt; Configurations Options -&amp;gt; Libaries -&amp;gt; libm.a mit dem Pfeil nach rechts einbinden. Siehe auch die [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|FAQ]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
/* lt. avr-libc Dokumentation:&lt;br /&gt;
char* dtostrf(&lt;br /&gt;
  double __val,&lt;br /&gt;
  char   __width,&lt;br /&gt;
  char   __prec,&lt;br /&gt;
  char * __s&lt;br /&gt;
)  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   // Pufferspeicher ausreichend groß&lt;br /&gt;
   // evtl. Vorzeichen + width + Endezeichen:&lt;br /&gt;
   char s[8]; &lt;br /&gt;
   float f = -12.345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   dtostrf( f, 6, 3, s ); &lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // verkürzt: uart_puts( dtostrf( f, 7, 3, s ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfangen ==&lt;br /&gt;
=== einzelne Zeichen empfangen ===&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von Zeichen muss der Empfangsteil des UART bei der Initialisierung aktiviert werden, indem das RXEN-Bit im jeweiligen Konfigurationsregister (UCSRB bzw UCSR0B/UCSR1B) gesetzt wird. Im einfachsten Fall wird solange gewartet, bis ein Zeichen empfangen wurde, dieses steht dann im UART-Datenregister (UDR bzw. UDR0 und UDR1 bei AVRs mit 2 UARTS) zur Verfügung (sogen. &amp;quot;Polling-Betrieb&amp;quot;). Ein Beispiel für den ATmega16:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Zusaetzlich zur Baudrateneinstellung und der weiteren Initialisierung: */&lt;br /&gt;
void Usart_EnableRX()&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= ( 1 &amp;lt;&amp;lt; RXEN );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion blockiert den Programmablauf. Alternativ kann das RXC-Bit in einer Programmschleife abgefragt werden und dann nur bei gesetztem RXC-Bit UDR ausgelesen werden. Eleganter und in den meisten Anwendungsfällen &amp;quot;stabiler&amp;quot; ist die Vorgehensweise, die empfangenen Zeichen in einer Interrupt-Routine einzulesen und zur späteren Verarbeitung in einem Eingangsbuffer (FIFO-Buffer) zwischenzuspeichern. Dazu existieren fertige und gut getestete [[Libraries|Bibliotheken]] &amp;lt;!-- &amp;quot;echte Libraries&amp;quot; (.a) wie im Verweis beschrieben sind hier eigentlich nicht gemeint, verwirrt hier etwas, da AVR-&amp;quot;Libraries&amp;quot; meist per #defines anpassbare Source-Codes sind, vielleicht so: --&amp;gt; und Quellcodekomponenten (z.B. UART-Library von P. Fleury, procyon-avrlib und einige in der &amp;quot;Academy&amp;quot; von avrfreaks.net).&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html Dokumenation der avr-libc/stdlib.h]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_printf Die Nutzung von printf]&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&amp;lt;!-- nimmermehr: * siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (printf etc.) im [[AVR-Tutorial:_UART]]. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: 9bit&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Zeichenketten (Strings) ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Zeichenketten, muß man sich zunächst darüber im klaren sein, daß es ein Kriterium geben muß, an dem der µC erkennen kann, wann ein String zu Ende ist. Sehr oft wird dazu das Zeichen &#039;Return&#039; benutzt, um das Ende eines Strings zu markieren. Dies ist vom Benutzer einfach eingebbar und er ist auch daran gewöhnt, daß er eine Eingabezeile mit einem Druck auf die Return Taste abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Prinzipiell gibt es jedoch keine Einschränkung bezüglich dieses speziellen Zeichens. Es muß nur sichergestellt werden, daß dieses spezielle &#039;Ende eines Strings&#039; - Zeichen nicht mit einem im Text vorkommenden Zeichen verwechselt werden kann. Wenn also im zu übertragenden Text beispielsweise kein &#039;;&#039; vorkommt, dann spricht nichts dagegen, einen String mit einem &#039;;&#039; abschließen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird die durchaus übliche Annahme getroffen, daß eine Stringübertragung identisch ist mit der Übertragung einer Textzeile und daher mit einem Return (&#039;\n&#039;) abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Das Problem der Übertragung eines Strings reduziert sich damit auf die Aufgabenstellung: Empfange und Sammle Zeichen in einem char Array, bis entweder das Array voll ist oder das &#039;String Ende Zeichen&#039; empfangen wurde. Danach wird der empfangene Text noch mit einem &#039;\0&#039; Zeichen abgeschlossen um einen Standard C-String daraus zu machen, mit dem dann weiter gearbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_gets( char* Buffer, uint8_t MaxLen )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t NextChar;&lt;br /&gt;
  uint8_t StringLen = 0;&lt;br /&gt;
&lt;br /&gt;
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen&lt;br /&gt;
&lt;br /&gt;
                                  // Sammle solange Zeichen, bis:&lt;br /&gt;
                                  // * entweder das String Ende Zeichen kam&lt;br /&gt;
                                  // * oder das aufnehmende Array voll ist&lt;br /&gt;
  while( NextChar != &#039;\n&#039; &amp;amp;&amp;amp; StringLen &amp;lt; MaxLen - 1 ) {&lt;br /&gt;
    *Buffer++ = NextChar;&lt;br /&gt;
    StringLen++;&lt;br /&gt;
    NextChar = uart_getc();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
                                  // Noch ein &#039;\0&#039; anhängen um einen Standard&lt;br /&gt;
                                  // C-String daraus zu machen&lt;br /&gt;
  *Buffer = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf ist darauf zu achten, dass das empfangende Array auch mit einer&lt;br /&gt;
vernünftigen Größe definiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
  char Line[40];      // String mit maximal 39 zeichen&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) );&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Interruptbetrieb==&lt;br /&gt;
&lt;br /&gt;
Beim ATMEGA8 ist es möglich, dass RXCIE Bit im Register UCSRB zu setzen.&lt;br /&gt;
Dieser Interrupt wird immer ausglöst, wenn Daten erfolgreich empfangen wurden.&lt;br /&gt;
Zusätzlich braucht man die Routine:&lt;br /&gt;
&lt;br /&gt;
ISR (USART_RXC_vect) {&lt;br /&gt;
   //irgendein Code&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
natürlich muss &amp;quot;Global Interrupt Enable&amp;quot; Aktiviert sein.&lt;br /&gt;
!! Nur getestet beim ATMEGA8 !!&lt;br /&gt;
&lt;br /&gt;
[BAUSTELLE! Aus Lerngründen eventuell als eigenen UART-Interrupt-Block hinter den grundlegenden Interrupt-Teil im Tutorial und hier eine kurze Einführung und einen Verweis darauf anbieten.]&lt;br /&gt;
&lt;br /&gt;
* Unterschied Polling-Betrieb (bisher, oben) und Interrupt-Betrieb&lt;br /&gt;
* Empfangen (Receive)&lt;br /&gt;
** Verändertes UART-Init, ISR (RXC), ggf. Fallstricke ([http://www.mikrocontroller.net/topic/84256#707214 UDR in der ISR lesen!]), Philosophie einer ISR (kurz und schmerzlos), Datenaustausch ISR zu Restprogramm (volatile)&lt;br /&gt;
** Einfachstbeispiel ([http://www.mikrocontroller.net/topic/84228#707052 Echo] (noch buggy beim Datenzugriff, siehe Lit. 2+3!)), ggf. LED zur ISR-Empfangsanzeige oder Overflow-Anzeige&lt;br /&gt;
* FIFO-Puffer, Ringpuffer&lt;br /&gt;
* Senden (Transmit)&lt;br /&gt;
** Variante &amp;quot;UART Data Register Empty&amp;quot; (UDRE) &lt;br /&gt;
** Variante &amp;quot;UART Transmit Complete&amp;quot; (TXC) &lt;br /&gt;
* Fertige UART-Bibliotheken (-&amp;gt;Fleury, -&amp;gt;Procyon)&lt;br /&gt;
* Literatur: &lt;br /&gt;
** 1/ [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=48188 avrfreaks.net Tutorial] inkl. Diskussion (engl.)&lt;br /&gt;
** 2/ avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_16bitio Why do some 16-bit timer registers sometimes get trashed?]&lt;br /&gt;
** 3/ [[Interrupt]] und atomarer Datenzugriff&lt;br /&gt;
&lt;br /&gt;
==Software-UART==&lt;br /&gt;
&lt;br /&gt;
Falls die Zahl der vorhandenen Hardware-UARTs nicht ausreicht, können weitere Schnittstellen über sogennante Software-UARTs ergänzt werden. Es gibt dazu (mindestens) zwei Ansätze: &lt;br /&gt;
* Der bei AVRs üblichste Ansatz basiert auf dem Prinzip, dass ein externer Interrupt-Pin für den Empfang (&amp;quot;RX&amp;quot;) genutzt wird. Das Startbit löst den Interrupt aus, in der Interrupt-Routine (ISR) wird der externe Interrupt deaktiviert und ein Timer aktiviert. In der Interrupt-Routine des Timers wird der Zustand des Empfangs-Pins entsprechend der Baudrate abgetastet. Nach Empfang des Stop-Bits wird der externe Interrupt wieder aktiviert. Senden kann über einen beliebigen Pin (&amp;quot;TX&amp;quot;) erfolgen, der entsprechend der Baudrate und dem zu sendenden Zeichen auf 0 oder 1 gesetzt wird. Die Implementierung ist nicht ganz einfach, es existieren dazu aber fertige Bibliotheken (z.B. bei [http://www.avrfreaks.net/ avrfreaks] oder in der [http://hubbard.engr.scu.edu/embedded/avr/avrlib/ Procyon avrlib]).&lt;br /&gt;
* Ein weiterer Ansatz erfordert keinen Pin mit &amp;quot;Interrupt-Funktion&amp;quot; aber benötigt mehr Rechenzeit. Jeder Input-Pin kann als Empfangspin (RX) dienen. Über einen Timer wird der Zustand des RX-Pins mit einem vielfachen der Baudrate abgetastet (dreifach scheint üblich) und High- bzw. Lowbits anhand einer Mindestanzahl identifiziert. (Beispiel: &amp;quot;Generic Software Uart&amp;quot; Application-Note von IAR)&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (z.B. ATtiny26 oder ATmega48,88,168,169) verfügen über ein Universal Serial Interface (USI), das teilweise UART-Funktion übernehmen kann. Atmel stellt eine Application-Note bereit, in der die Nutzung des USI als UART erläutert wird (im Prinzip &amp;quot;Hardware-unterstützter Software-UART&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==Handshaking==&lt;br /&gt;
Wenn der Sender ständig sendet, wird irgendwann der Fall eintreten, daß der Empfänger nicht bereit ist, neue Zeichen zu empfangen. In diesem Fall muß durch ein &#039;&#039;&#039;Handshake-Verfahren&#039;&#039;&#039; die Situation bereinigt werden. Handshake bedeutet nichts anderes, als daß der Empfänger dem Sender mitteilt, daß er zur Zeit keine Daten annehmen kann und der Sender die Übertragung der nächsten Zeichen solange einstellen soll, bis der Empfänger signalisiert, daß er wieder Zeichen aufnehmen kann.&lt;br /&gt;
===Hardwarehandshake (RTS/CTS)===&lt;br /&gt;
Beim Hardwarehandshake werden zusätzlich zu den beiden Daten-Übertragungsleitungen noch 2 weitere Leitungen benötigt: &#039;&#039;&#039;RTS&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;equest &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end) und &#039;&#039;&#039;CTS&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end). Jeder der beiden Kommunikationspartner ist verpflichtet, bevor ein Zeichen gesendet wird, den Zustand der &#039;&#039;&#039;RTS&#039;&#039;&#039; Leitung zu überprüfen. Nur wenn die Gegenstelle darauf Empfangsbereitschaft signalisiert, darf das Zeichen gesendet werden. Um der Gegenstelle zu signalisieren, daß sie zur Zeit keine Zeichen schicken soll, wird die Leitung &#039;&#039;&#039;CTS&#039;&#039;&#039; benutzt.&lt;br /&gt;
&lt;br /&gt;
===Softwarehandshake (XON/XOFF)===&lt;br /&gt;
Beim Softwarehandshake sind keine speziellen Leitungen notwendig. Statt dessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, daß Senden einzustellen bzw. wieder aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;XOFF&#039;&#039;&#039; Aufforderung das Senden einzustellen&lt;br /&gt;
* &#039;&#039;&#039;XON&#039;&#039;&#039;  Gegenstelle darf wieder senden&lt;br /&gt;
&lt;br /&gt;
Nachteilig bei einem Softwarehandshake ist es, daß dadurch keine direkte binäre Datenübertragung mehr möglich ist. Von den möglichen 256 Bytewerten werden ja 2 (nämlich &#039;&#039;&#039;XON&#039;&#039;&#039; und &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus.&lt;br /&gt;
&lt;br /&gt;
==Fehlersuche==&lt;br /&gt;
Erstaunlich oft wird im Forum der Hilferuf laut: &amp;quot;Meine UART funktioniert nicht, was mache ich falsch&amp;quot;. In der überwiegenden Mehrzahl der Fälle stellt sich dann heraus, daß es sich um ein Hardwareproblem handelt, wobei da wiederrum der Löwenanteil auf das Konto einer nicht korrekt eingestellten Taktrate geht: Der µC benutzt nicht einen angeschlossenen Quarz, so wie er auch im Programm eingetragen ist, sondern läuft immer noch mit dem internen RC-Takt. Daraus resultiert aber auch, daß der Baudraten Konfigurationswert falsch berechnet wird.&lt;br /&gt;
&lt;br /&gt;
Eine Checkliste zum Aufspüren solcher Fehler findet sich [[AVR_Checkliste#UART.2FUSART|hier]].&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Analoge Eingangswerte werden in der Regel über den AVR Analog-Digital-Converter (AD-Wandler, ADC) eingelesen, der in vielen Typen verfügbar ist (typisch 10bit Auflösung). Durch diesen werden analogen Signale (Spannungen) in digitale Zahlenwerte gewandelt. Bei AVRs, die über keinen internen AD-Wandler verfügen (z.B. ATmega162, ATtiny2313), kann durch externe Beschaltung (R/C-Netzwerk und &amp;quot;Zeitmessung&amp;quot;) die Funktion des AD-Wandlers &amp;quot;emuliert&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
Es existieren keine AVRs mit eingebautem Digital-Analog-Konverter (DAC). Diese Funktion muss durch externe Komponenten nachgebildet werden (z.B. PWM und &amp;quot;Glättung&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Unabhängig davon besteht natürlich immer die Möglichkeit, spezielle Bausteine zur Analog-Digital- bzw. Digital-Analog-Wandlung zu nutzen und diese über eine digitale Schnittstelle (z.b. SPI oder I2C) mit einem AVR anzusteuern.&lt;br /&gt;
&lt;br /&gt;
== AC (Analog Comparator) ==&lt;br /&gt;
&lt;br /&gt;
Der Comparator vergleicht 2 Spannungen an den Pins AIN0 und AIN1 und gibt einen Status aus welche der beiden Spannungen größer ist. AIN0 Dient dabei als Referenzspannung (Sollwert) und AIN1 als Vergleichsspannung (Istwert). Als Referenzspannung kann auch alternativ eine interne Referenzspannung ausgewählt werden.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Liegt die Vergleichsspannung (IST) unter der der Referenzspannung (SOLL) gibt der Comperator eine logische 0 aus. ISt die Vergleichsspannung hingegen größer als die Referenzspannung wird eine logische 1 ausgegeben.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Der Comperator arbeitet völlig autark bzw. parallel zum Prozessor. Für mobile Anwendungen empfiehlt es sich ihn abzuschalten sofern er nicht benötigt wird, da er ansonsten Strom benötigt. Der Comparator kann Interruptgesteuert abgefragt werden oder im Pollingbetrieb.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Steuer- bzw. Statusregister ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ACSR (0x28) - Analog Comparator Status Register&amp;lt;BR&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACD&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;AINBG&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACO&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACI&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | n/a&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 7 ACD - Analog Comperator Disable: 0 = Comparator ein, 1 = Comparator aus. Wird dieses Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 ACIE ggf. abgeschaltet werden.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 6 AINBG - Analog Comparator Bandgap Select: Ermöglicht das umschalten zwischen interner und externer Referenzspannung. 1 = interne (~1,3 Volt), 0 = externe Referenzspannung (an Pin AIN0)&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 5 ACO - Analog Coparator Output: Hier wird das Ergebnis des Vergleichs angezeigt. Es liegt typischerweise nach 1-2 Taktzyklen vor. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ergebnis: &amp;lt;BR&amp;gt;IST &amp;lt; SOLL --&amp;gt; 0&amp;lt;BR&amp;gt;&lt;br /&gt;
IST &amp;gt; SOLL --&amp;gt; 1&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4 ACI - Analog Comparator Interrupt Flag: Dieses Bit wird von der Hardware gesetzt wenn ein Interruptereignis das in Bit 0 und 1 definiert ist eintritt. Dieses Bit löst noch keinen Interrupt aus! Die Interruptroutine wird nur dann ausgeführt wenn das Bit 3 ACIE gesetzt ist und global Interrupts erlaubt sind (I-Bit in SREG=1). Das Bit 4 ACI wird wieder gelöscht wenn die Interruptroutine ausgeführt wurde oder wenn manuell das Bit auf 1 gesetzt wird. Das Bit kann für Abfragen genutzt werden, steuert oder konfuguriert aber nicht den Comparator.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 3 ACIE - Analog Comparator Interrupt Enable: Ist das Bit auf 1 gesetzt wird immer ein Interrupt ausgelöst wenn das Ereignis das in Bit 1 und 0 definiert ist eintritt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 2 ACIC - Analog Comparator Input Capture Enable: Wird das Bit gesetzt wird der Comparatorausgang intern mit dem Counter 1 verbunden. Es könnten damit z.b. die Anzahl der Vergleiche im Counter1 gezählt werden. Um den Comparator an den Timer1 Input Capture Interrupt zu verbinden muß im Timerregister das TICIE1 Bit auf 1 gesetzt werden. Der Trigger wird immer dann ausgelöst wenn das in Bit 1 und 0 definierte Ereignis eintritt.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 1,0 ACIS1,ACIS0 - Analog Comparator Interrupt select: Hier wird definiert welche Ereignisse einen Interrupt auslösen sollen:&amp;lt;BR&amp;gt;&lt;br /&gt;
00 = Interrupt auslösen bei jedem Flankenwechsel&amp;lt;BR&amp;gt;&lt;br /&gt;
10 = Interrupt auslösen bei fallender Flanke&amp;lt;BR&amp;gt;&lt;br /&gt;
11 = Interrupt auslösen bei steigender Flanke&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Werden diese Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 gelöscht werden.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der Analog-Digital-Konverter (ADC) wandelt analoge Signale in digitale Werte um, welche vom Controller interpretiert werden können. Einige AVR-Typen haben bereits einen mehrkanaligen Analog-Digital-Konverter eingebaut. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst werden kann, wird durch die Auflösung des ADC in Anzahl Bits angegeben, man hört bzw. liest jeweils von 8-Bit-ADC oder 10-Bit-ADC oder noch höher. ADCs die in AVRs enthalten sind haben zur Zeit eine maximale Auflösung von 10-Bit.&lt;br /&gt;
&lt;br /&gt;
Ein ADC mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des Analog-Digital-Konverters ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Der interne ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem Analog-Digital-Wandler (ADC) zurückgreifen, die über mehrere Kanäle verfügen. Kanäle heißt in diesem Zusammenhang, dass zwar bis zu zehn analoge Eingänge am AVR verfügbar sind, aber nur ein &amp;quot;echter&amp;quot; Analog-Digital-Wandler zur Verfügung steht, vor der eigentlichen Messung ist also einzustellen, welcher Kanal (&amp;quot;Pin&amp;quot;) mit dem Wandler verbunden und gemessen wird.&lt;br /&gt;
&lt;br /&gt;
Die Umwandlung innerhalb des AVR basiert auf der schrittweisen Näherung. Beim AVR müssen die Pins &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AVCC&#039;&#039;&#039; beschaltet werden. Für genaue Messungen sollte AVCC über ein L-C Netzwerk mit VCC verbunden werden, um Spannungsspitzen und -einbrüche vom Analog-Digital-Wandler fernzuhalten. Im Datenblatt findet sich dazu eine Schaltung, die 10uH und 100nF vorsieht.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Analog-Digital-Wandlung wird auf eine Referenzspannung bezogen. Aktuelle AVRs bieten drei Möglichkeiten zur Wahl dieser Spannung:&lt;br /&gt;
&lt;br /&gt;
* Eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; am Anschlusspin &#039;&#039;&#039;AREF&#039;&#039;&#039;. Die minimale (externe) Referenzspannung darf jedoch nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. &lt;br /&gt;
&lt;br /&gt;
* Verfügt der AVR über eine interne Referenzspannung, kann diese genutzt werden. Alle aktuellen AVRs mit internem AD-Wandler sollten damit ausgestattet sein (vgl. Datenblatt: 2,56V oder 1,1V je nach Typ). Das Datenblatt gibt auch über die Genauigkeit dieser Spannung Auskunft.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;interne&#039;&#039;&#039; Referenzspannung wird auf &#039;&#039;&#039;Vcc&#039;&#039;&#039; bezogen, eine externe Referenzspannung auf &#039;&#039;&#039;GND (Masse)&#039;&#039;&#039;. Davon unabhängig werden digitalisierte Spannungen &#039;&#039;&#039;immer&#039;&#039;&#039; auf GND bezogen. Beim ATMEGA8 z.B. ist der Pin AREF über 32kOhm mit GND verbunden, d.h. man muss diese doch extrem niedrige Eingangsimpedanz mit in die Berechnung für einen Spannungsteiler einbeziehen, bzw. kann diesen Widerstand als R2 gleich mit benutzen. Formel für Spannungsteiler: Udiv = U / ((R1 + R2) / R2)&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung von Vcc oder der internen Referenz wird empfohlen, einen Kondensator zwischen dem AREF-Pin und GND anzuordnen. Die Festlegung, welche Spannungsreferenz genutzt wird, erfolgt z.B. beim ATmega16 mit den Bits REFS1/REFS0 im ADMUX-Register. Die zu messende Spannung muss im Bereich zwischen &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AREF&#039;&#039;&#039; (egal ob intern oder extern) liegen. &lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; kann in zwei verschiedenen Betriebsarten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
; Einfache Wandlung (Single Conversion) : In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestoßen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
; Frei laufend (Free Running) : In dieser Betriebsart erfasst der Wandler permanent die anliegende Spannung und schreibt diese in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Die Register des ADC ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; verfügt über eigene Register. Im Folgenden die Registerbeschreibung eines  ATMega16, welcher über 8 ADC-Kanäle verfügt. Die Register unterscheiden sich jedoch nicht erheblich von denen anderer AVRs (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister A.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den &#039;&#039;&#039;ADC&#039;&#039;&#039; verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADEN&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able)&lt;br /&gt;
:Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren. Wenn das Bit nicht gesetzt ist, können die Pins wie normale I/O-Pins verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADSC&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion)&lt;br /&gt;
:Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.&lt;br /&gt;
:Wenn das Bit nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.&lt;br /&gt;
:Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADFR&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;F&#039;&#039;&#039;ree &#039;&#039;&#039;R&#039;&#039;&#039;un select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Ist das Bit auf 1 gesetzt arbeitet der ADC im Freerunning Modus. Dabe iwird das Datenregister permanent aktualisiert. Ist das Bit hingegen auf 0 gesetzt macht der ADC nur eine Single Conversion. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIF&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag)&lt;br /&gt;
:Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt, sobald eine Umwandlung erfolgt ist und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert wurde. Das Bit wird bei lesendem Zugriff auf &#039;&#039;&#039;ADC(L,H)&#039;&#039;&#039; automatisch (d.h. durch die Hardware) gelöscht.&lt;br /&gt;
:Wenn das &#039;&#039;&#039;ADIE&#039;&#039;&#039; Bit sowie das &#039;&#039;&#039;I-Bit&#039;&#039;&#039; im AVR &#039;&#039;&#039;Statusregister&#039;&#039;&#039; gesetzt ist, wird der &#039;&#039;&#039;ADC Interrupt&#039;&#039;&#039; ausgelöst und die Interrupt-Behandlungsroutine aufgerufen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn die Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 in das Register geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIE&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;, dann wird der &#039;&#039;&#039; ADC-Interrupt&#039;&#039;&#039; aktiviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADPS2...ADPS0&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;P&#039;&#039;&#039;rescaler &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
:Der &#039;&#039;&#039;ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz ergibt.&lt;br /&gt;
:Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
TF_{min}=\frac{CLK}{200\,\mathrm{kHz}}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50\,\mathrm{kHz}}=\frac{4000000}{50000}=\mathbf{80}&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im Interesse der schnelleren Wandlungszeit werden wir hier den Faktor 32 einstellen.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCL&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ADCH&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC &#039;&#039;&#039; Data Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn eine Umwandlung abgeschlossen ist, befindet sich der gemessene Wert in&lt;br /&gt;
diesen beiden Registern. Von &#039;&#039;&#039;ADCH&#039;&#039;&#039; werden nur die beiden niederwertigsten Bits verwendet. Es müssen immer beide Register ausgelesen werden, und zwar immer &#039;&#039;&#039;in der Reihenfolge: ADCL, ADCH&#039;&#039;&#039;. &lt;br /&gt;
Der effektive Messwert ergibt sich dann zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCL;       // mit uint16_t x&lt;br /&gt;
x += (ADCH&amp;lt;&amp;lt;8); // in zwei Zeilen (LSB/MSB-Reihenfolge und&lt;br /&gt;
                // C-Operatorpriorität sichergestellt)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADMUX&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Mu&#039;&#039;&#039;ltiple&#039;&#039;&#039;x&#039;&#039;&#039;er Select Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Register wird der zu messende Kanal ausgewählt. Beim 90S8535&lt;br /&gt;
kann jeder Pin von Port A als &#039;&#039;&#039;ADC&#039;&#039;&#039;-Eingang verwendet werden (=8 Kanäle).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADLAR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX4&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX3&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;REFS1...REFS0&#039;&#039;&#039; (&#039;&#039;&#039;Ref&#039;&#039;&#039;erence&#039;&#039;&#039;S&#039;&#039;&#039;election Bits)&lt;br /&gt;
:Mit diesen Bits kann die Referenzspannung eingestellt werden:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Referenzspanung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Externes AREF&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | AVCC als Referenz&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Interne 2,56 Volt&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADLAR&#039;&#039;&#039; (&#039;&#039;&#039;ADC &#039;&#039;&#039; &#039;&#039;&#039;L&#039;&#039;&#039;eft &#039;&#039;&#039;A&#039;&#039;&#039;djust &#039;&#039;&#039;R&#039;&#039;&#039;esult)&lt;br /&gt;
:Das ADLAR Bit verändert das Aussehen des Ergebnisses der AD-Wandlung. Bei einer logischen 1 wird das Ergebnis linksbündig ausgegeben, bei einer 0 rechtsündig. Eine Änderung in diesem Bit beeinflusst das Ergebnis sofort, ganz egal ob bereits eine Wandlung läuft.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX4...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 5 Bits wird der zu messende Kanal bestimmt. Wenn man einen einfachen 1-kanaligen ADC verwendet wird einfach die entsprechende Pinnummer des Ports in die Bits 0...2 eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
:Eine Empfehlung ist deswegen diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren des ADC ====&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039; ADC&#039;&#039;&#039; zu aktivieren, müssen wir das &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bit im &#039;&#039;&#039;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
Ein kleines Beispiel für den &amp;quot;single conversion&amp;quot;-Mode bei einem ATmega169 und Nutzung der internen Referenzspannung (beim &#039;169 1,1V bei anderen AVRs auch 2,56V). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler einstellen. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1023 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint16_t ReadChannel(uint8_t mux)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
&lt;br /&gt;
  ADMUX = mux;                      // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler &lt;br /&gt;
                               // setzen auf 8 (1) und ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  /* nach Aktivieren des ADC wird ein &amp;quot;Dummy-Readout&amp;quot; empfohlen, man liest&lt;br /&gt;
     also einen Wert und verwirft diesen, um den ADC &amp;quot;warmlaufen zu lassen&amp;quot; */&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
     ;     // auf Abschluss der Konvertierung warten &lt;br /&gt;
  }&lt;br /&gt;
  result = ADCW;  // ADCW muss einmal gelesen werden,&lt;br /&gt;
                  // sonst wird Ergebnis der nächsten Wandlung&lt;br /&gt;
                  // nicht übernommen.&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  result = 0; &lt;br /&gt;
  for( i=0; i&amp;lt;4; i++ )&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);            // eine Wandlung &amp;quot;single conversion&amp;quot;&lt;br /&gt;
    while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
      ;   // auf Abschluss der Konvertierung warten&lt;br /&gt;
    }&lt;br /&gt;
    result += ADCW;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Beispielaufrufe: */&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
&lt;br /&gt;
  adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -&amp;gt; Channel 0 */&lt;br /&gt;
  ...&lt;br /&gt;
  adcval = ReadChannel(2); /* MUX-Bits auf 0b0010 -&amp;gt; Channel 2 */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekennzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Das Löschen des ADIF-Flags sollte, &#039;&#039;&#039;entgegen&#039;&#039;&#039; der [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_intbits FAQ], mit&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADIF);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
erfolgen. Die Methode in der FAQ eignet sich nur für Register in denen &#039;&#039;&#039;nur&#039;&#039;&#039; Interrupt-Flags stehen.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Analog-Digital-Wandlung ohne internen ADC ===&lt;br /&gt;
&lt;br /&gt;
==== Messen eines Widerstandes ====&lt;br /&gt;
&lt;br /&gt;
Analoge Werte lassen sich ohne Analog-Digital-Wandler auch indirekt ermitteln. Im Folgenden wird die Messung des an einem Potentiometer eingestellten Widerstands anhand der Ladekurve eines Kondensators erläutert. Bei dieser Methode wird nur ein Portpin benötigt, ein Analog-Digital-Wandler oder Analog-Comparator ist nicht erforderlich. Es wird dazu ein Kondensator und der Widerstand (das Potentiometer) in Reihe zwischen Vorsorgungsspannung und Masse/GND geschaltet (sogen. RC-Netzwerk). Zusätzlich wird eine Verbindung der Leitung zwischen Kondensator und Potentiometer zu einem Portpin des Controllers hergestellt. Die folgende Abbildung verdeutlicht die erforderliche Schaltung. &lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wird der Portpin des Controllers auf Ausgang konfiguriert (im Beispiel &#039;&#039;DDRD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) und dieser Ausgang auf Logisch 1 (&amp;quot;High&amp;quot;, &#039;&#039;PORTD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) geschaltet, liegt an beiden &amp;quot;Platten&amp;quot; des Kondensators das gleiche Potential &#039;&#039;&#039;VCC&#039;&#039;&#039; an und der Kondensator somit entladen. (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so, da an beiden Seiten des Kondensators das gleiche Potential anliegt und somit eine Potentialdifferenz von 0V besteht =&amp;gt; Kondensator ist entladen).&lt;br /&gt;
&lt;br /&gt;
Nach einer gewissen Zeit ist der Kondensator entladen und der Portpin wird als Eingang konfiguriert (&#039;&#039;DDRD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2); PORTD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2)&#039;&#039;), wodurch dieser hochohmig wird. Der Status des Eingangspin (in PIND) ist Logisch 1 (High). Der Kondensator lädt sich jetzt über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), wird das Eingangssignal als LOW erkannt (Bit in PIND wird 0). Die Zeitspanne zwischen der Umschaltung von Entladung auf Aufladung und dem Wechsel des Eingangssignals von High auf Low ist ein Maß für den am Potentiometer eingestellten Widerstand. Zur Zeitmessung kann einer der im Controller vorhandenen Timer genutzt werden. Der 220 Ohm Widerstand dient dem Schutz des Controllers. Es würde sonst bei Maximaleinstellung des Potentionmeters (hier 0 Ohm) ein zu hoher Strom fließen, der die Ausgangsstufe des Controllers zerstört. &lt;br /&gt;
&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B: 0...100 % oder so) umzurechnen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Link 404 =&amp;gt; auskommentiert, mthomas 9.2.2008 &lt;br /&gt;
Ein Beispielprogramm findet sich auf [http://www.mypage.bluewin.ch/ch_schifferle/ Christian Schifferles Web-Seite] im Archiv &#039;&#039;ATMEL.ZIP&#039;&#039;, welches unter den Titel &#039;&#039;Tutorial &amp;quot;Programmieren mit C für Atmel Mikrocontroller&#039;&#039; heruntergeladen werden kann. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ADC über Komparator ====&lt;br /&gt;
&lt;br /&gt;
Es gibt einen weiteren Weg, eine analoge Spannung mit Hilfe des&lt;br /&gt;
Komparators, welcher in fast jedem AVR integriert ist, zu messen. Siehe dazu&lt;br /&gt;
auch die Application Note AVR400 von Atmel.&lt;br /&gt;
&lt;br /&gt;
Dabei wird das zu messende Signal auf den invertierenden Eingang&lt;br /&gt;
des Komparators geführt. Zusätzlich wird ein Referenzsignal an den nicht&lt;br /&gt;
invertierenden Eingang des Komparators angeschlossen. Das Referenzsignal wird&lt;br /&gt;
hier auch wieder über ein RC-Glied erzeugt, allerdings mit festen Werten für R&lt;br /&gt;
und C.&lt;br /&gt;
&lt;br /&gt;
[[Image:ADC ueber Komparator.gif]]&lt;br /&gt;
&lt;br /&gt;
Das Prinzip der Messung ist nun dem vorhergehenden recht&lt;br /&gt;
ähnlich. Durch Anlegen eines LOW-Pegels an Pin 2 wird der Kondensator zuerst&lt;br /&gt;
einmal entladen. Auch hier muss darauf geachtet werden, dass der Entladevorgang&lt;br /&gt;
genügend lang dauert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Nun wird Pin 2 auf HIGH gelegt. Der Kondensator wird geladen. Wenn die Spannung&lt;br /&gt;
über dem Kondensator die am Eingangspin anliegende Spannung erreicht hat&lt;br /&gt;
schaltet der Komparator durch. Die Zeit, welche benötigt wird, um den&lt;br /&gt;
Kondensator zu laden kann nun auch wieder als Maß für die Spannung an Pin 1&lt;br /&gt;
herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Ich habe es mir gespart, diese Schaltung auch aufzubauen und&lt;br /&gt;
zwar aus mehreren Gründen:&lt;br /&gt;
&lt;br /&gt;
# 3 Pins notwendig.&lt;br /&gt;
# Genauigkeit vergleichbar mit einfacherer Lösung.&lt;br /&gt;
# War einfach zu faul.&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung liegt allerdings darin, dass damit&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines Digital-Analog-Konverters (&#039;&#039;&#039;DAC&#039;&#039;&#039;) können wir nun auch Analogsignale ausgeben. Es gibt hier mehrere Verfahren. &amp;lt;!-- Wenn wir beim ADC die Möglichkeit haben, mit externen Komponenten zu operieren, müssen wir bei der DAC-Wandlung mit dem auskommen, was der Controller selber zu bieten hat. --mt: hmm, richtig? verstaendlich? redundant? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DAC über mehrere digitale Ausgänge ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir an den Ausgängen des Controllers ein entsprechendes&lt;br /&gt;
Widerstandsnetzwerk aufbauen haben wir die Möglichkeit, durch die Ansteuerung&lt;br /&gt;
der Ausgänge über den Widerständen einen Addierer aufzubauen, mit dessen&lt;br /&gt;
Hilfe wir eine dem Zahlenwert proportionale Spannung erzeugen können. Das&lt;br /&gt;
Schaltbild dazu kann etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
[[Image:DAC R2R.gif]]&lt;br /&gt;
&lt;br /&gt;
Es sollten selbstverständlich möglichst genaue Widerstände verwendet&lt;br /&gt;
werden, also nicht unbedingt solche mit einer Toleranz von 10% oder mehr.&lt;br /&gt;
Weiterhin empfiehlt es sich, je nach Anwendung den Ausgangsstrom über einen&lt;br /&gt;
Operationsverstärker zu verstärken.&lt;br /&gt;
&lt;br /&gt;
=== PWM (Pulsweitenmodulation) ===&lt;br /&gt;
&lt;br /&gt;
Wir kommen nun zu einem Thema, welches in aller Munde ist, aber viele&lt;br /&gt;
Anwender verstehen nicht ganz, wie &#039;&#039;&#039; PWM&#039;&#039;&#039; eigentlich funktioniert.&lt;br /&gt;
&lt;br /&gt;
Wie wir alle wissen, ist ein Mikrocontroller ein rein digitales Bauteil.&lt;br /&gt;
Definieren wir einen Pin als Ausgang, dann können wir diesen Ausgang entweder&lt;br /&gt;
auf HIGH setzen, worauf am Ausgang die Versorgungsspannung &#039;&#039;&#039; Vcc&#039;&#039;&#039; anliegt, oder aber wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt. Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen HIGH und LOW umschalten? - Richtig, wir erhalten eine Rechteckspannung, wie die folgende Abbildung zeigt:&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 1.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Rechteckspannung hat nun einen arithmetischen Mittelwert, &lt;br /&gt;
der je nach Pulsbreite kleiner oder größer ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 2.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun diese pulsierende Ausgangsspannung noch über ein RC-Glied filtern/&amp;quot;glätten&amp;quot;, dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVRs können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. &lt;br /&gt;
Dazu dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt, weshalb ein Blick ins entsprechende Datenblatt dringend angeraten wird.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren, müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| PWM-Modus des Timers ist nicht aktiv.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Timer/Counter zählt nun permanent von 0 bis zur Obergrenze&lt;br /&gt;
und wieder zurück, er wird also als sogenannter Auf-/Ab Zähler betrieben. &lt;br /&gt;
Die Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss mit den Bits &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; desselben&lt;br /&gt;
Registers die gewünschte Ausgabeart des Signals definiert werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Nicht invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Hochzählen und gesetzt beim&lt;br /&gt;
Herunterzählen.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Herunterzählen und gesetzt beim&lt;br /&gt;
Hochzählen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl, um beispielsweise den Timer/Counter als&lt;br /&gt;
nicht invertierenden 10-Bit PWM zu verwenden, heißt dann:&lt;br /&gt;
&lt;br /&gt;
alte Schreibweise (PWMxx wird nicht mehr akzeptiert)&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;PWM11)|(1&amp;lt;&amp;lt;PWM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
neue Schreibweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;WGM11)|(1&amp;lt;&amp;lt;WGM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Timer/Counter überhaupt läuft, müssen wir im Control&lt;br /&gt;
Register B &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; noch den gewünschten Takt (Vorteiler) einstellen und&lt;br /&gt;
somit auch die Frequenz des &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signals bestimmen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stop. Der Timer/Counter wird gestoppt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin 1, negative Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin 1, positive Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Also um einen Takt von CK / 1024 zu generieren, verwenden wir&lt;br /&gt;
folgenden Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1B = (1&amp;lt;&amp;lt;CS12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt muss nur noch der Vergleichswert festgelegt werden. Diesen&lt;br /&gt;
schreiben wir in das 16-Bit Timer/Counter Output Compare Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem Vergleichswert und dem generierten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal aufzeigen.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
Ach ja, fast hätte ich&#039;s vergessen. Das generierte &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal&lt;br /&gt;
wird am Output Compare Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; des Timers ausgegeben und leider können wir&lt;br /&gt;
deshalb auch beim AT90S2313 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren. Andere AVR-Typen verfügen über bis zu vier PWM-Ausgänge. Zu beachten ist außerdem, das wenn der OC Pin aktiviert ist, er nichtmehr wie üblich funktioniert und z.B. nicht einfach über PINx ausgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Ein Programm, dass an einem ATMega8 den Fast-PWM Modus verwendet, den Modus 14, könnte so aussehen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // OC1A auf Ausgang&lt;br /&gt;
  DDRB = (1 &amp;lt;&amp;lt; PB1 );&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // Timer 1 einstellen&lt;br /&gt;
  //  &lt;br /&gt;
  // Modus 14:&lt;br /&gt;
  //    Fast PWM, Top von ICR1&lt;br /&gt;
  //&lt;br /&gt;
  //     WGM13    WGM12   WGM11    WGM10&lt;br /&gt;
  //      1        1       1        0&lt;br /&gt;
  //&lt;br /&gt;
  //    Timer Vorteiler: 1&lt;br /&gt;
  //     CS12     CS11    CS10&lt;br /&gt;
  //       0        0       1&lt;br /&gt;
  //&lt;br /&gt;
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match&lt;br /&gt;
  //     COM1A1   COM1A0&lt;br /&gt;
  //       1        0&lt;br /&gt;
 &lt;br /&gt;
  TCCR1A = (1&amp;lt;&amp;lt;COM1A1) | (1&amp;lt;&amp;lt;WGM11);&lt;br /&gt;
  TCCR1B = (1&amp;lt;&amp;lt;WGM13) | (1&amp;lt;&amp;lt;WGM12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  //  den Endwert (TOP) für den Zähler setzen&lt;br /&gt;
  //  der Zähler zählt bis zu diesem Wert&lt;br /&gt;
&lt;br /&gt;
  ICR1 = 0x6FFF;&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  // der Compare Wert&lt;br /&gt;
  // Wenn der Zähler diesen Wert erreicht, wird mit&lt;br /&gt;
  // obiger Konfiguration der OC1A Ausgang abgeschaltet&lt;br /&gt;
  // Sobald der Zähler wieder bei 0 startet, wird der&lt;br /&gt;
  // Ausgang wieder auf 1 gesetzt&lt;br /&gt;
  //&lt;br /&gt;
  // Durch Verändern dieses Wertes, werden die unterschiedlichen&lt;br /&gt;
  // PWM Werte eingestellt.&lt;br /&gt;
&lt;br /&gt;
  OCR1A = 0x3FFF;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  while( 1 )&lt;br /&gt;
    ;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für Details der PWM Möglichkeiten, muß immer das jeweilge Datenblatt des Prozessors konsultiert werden, da sich die unterschiedlichen Prozessoren in ihren Möglichkeiten doch stark unterscheiden. Auch muß man aufpassen, welches zu setzende Bit in welchem Register sind. Auch hier kann es sein, dass gleichnamige Konfigurationsbits in unterschiedlichen Konfigurationsregistern (je nach konkretem Prozessortyp) sitzen.&lt;br /&gt;
&lt;br /&gt;
= LCD-Ansteuerung =&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung an der LCD-Controller-Platine ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; (ist in manchen Unterlagen auch &#039;&#039;&#039;EN&#039;&#039;&#039;  für &#039;&#039;Enable&#039;&#039; abgekürzt) benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert, ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitetet hat (diese Methode u.a. in der LCD-Library von Peter Fleury verwendet). Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt da wir wissen, welche Anschlüsse das LCDs benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin #-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin-µC&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND oder Poti (siehe oben)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD4 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD5 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD0 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD1 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD2 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD3 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man die Steuerleitungen EN und RS auf Pins an einem anderen Port legen möchte, kann man so wie in diesem [http://www.mikrocontroller.net/topic/88543#751982 Forumsbeitrag] vorgehen.&lt;br /&gt;
&lt;br /&gt;
Ok, alles ist verbunden, wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display? &lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
void lcd_data(unsigned char temp1);&lt;br /&gt;
void lcd_string(char *data);&lt;br /&gt;
void lcd_command(unsigned char temp1);&lt;br /&gt;
void lcd_enable(void);&lt;br /&gt;
void lcd_init(void);&lt;br /&gt;
void lcd_home(void);&lt;br /&gt;
void lcd_clear(void);&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y);&lt;br /&gt;
&lt;br /&gt;
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
&lt;br /&gt;
// LCD Befehle&lt;br /&gt;
&lt;br /&gt;
#define CLEAR_DISPLAY 0x01&lt;br /&gt;
#define CURSOR_HOME   0x02&lt;br /&gt;
&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
&lt;br /&gt;
#define LCD_PORT      PORTD&lt;br /&gt;
#define LCD_DDR       DDRD&lt;br /&gt;
#define LCD_RS        PD4&lt;br /&gt;
#define LCD_EN        PD5&lt;br /&gt;
// DB4 bis DB7 des LCD sind mit PD0 bis PD3 des AVR verbunden&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// sendet ein Datenbyte an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_data(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_RS);        // RS auf 1 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_command(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);        // RS auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;              // oberes Nibble holen&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;            // maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;            // unteres Nibble holen und maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// erzeugt den Enable-Puls&lt;br /&gt;
void lcd_enable(void)&lt;br /&gt;
{&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
    _delay_us(1);                   // kurze Pause&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/80900&lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Initialisierung: &lt;br /&gt;
// Muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
void lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
   LCD_DDR = LCD_DDR | 0x0F | (1&amp;lt;&amp;lt;LCD_RS) | (1&amp;lt;&amp;lt;LCD_EN);   // Port auf Ausgang schalten&lt;br /&gt;
&lt;br /&gt;
   // muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(15);&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x03;            &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);      // RS auf 0&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4 Bit Modus aktivieren &lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x02;&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4Bit / 2 Zeilen / 5x7&lt;br /&gt;
   lcd_command(0x28);&lt;br /&gt;
    &lt;br /&gt;
   // Display ein / Cursor aus / kein Blinken&lt;br /&gt;
   lcd_command(0x0C); &lt;br /&gt;
 &lt;br /&gt;
   // inkrement / kein Scrollen&lt;br /&gt;
   lcd_command(0x06);&lt;br /&gt;
&lt;br /&gt;
   lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
&lt;br /&gt;
void lcd_clear(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CLEAR_DISPLAY);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
&lt;br /&gt;
void lcd_home(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CURSOR_HOME);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)&lt;br /&gt;
&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t tmp;&lt;br /&gt;
&lt;br /&gt;
  switch (y) {&lt;br /&gt;
    case 1: tmp=0x80+0x00+x; break;    // 1. Zeile&lt;br /&gt;
    case 2: tmp=0x80+0x40+x; break;    // 2. Zeile&lt;br /&gt;
    case 3: tmp=0x80+0x10+x; break;    // 3. Zeile&lt;br /&gt;
    case 4: tmp=0x80+0x50+x; break;    // 4. Zeile&lt;br /&gt;
  }&lt;br /&gt;
  lcd_command(tmp);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_string(char *data)&lt;br /&gt;
{&lt;br /&gt;
    while(*data) {&lt;br /&gt;
        lcd_data(*data);&lt;br /&gt;
        data++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches die Funktionen benutzt, sieht zb. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen&lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    lcd_data(&#039;T&#039;);&lt;br /&gt;
    lcd_data(&#039;e&#039;);&lt;br /&gt;
    lcd_data(&#039;s&#039;);&lt;br /&gt;
    lcd_data(&#039;t&#039;);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
&lt;br /&gt;
    lcd_string(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist dabei, dass die Optimierung bei der Compilierung eingeschaltet ist, sonst stimmen die Zeiten der Funktionen _delay_us() und _delay_ms() nicht und der Code wird wesentlich länger (Siehe Dokumentation der libc im WinAVR).&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches eine Variable ausgibt, sieht zb. so aus.&lt;br /&gt;
Mittels der itoa() Funktion (itoa = &amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;nteger &amp;lt;b&amp;gt;To&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;A&amp;lt;/b&amp;gt;scii ) wird von einem Zahlenwert eine textuelle Repräsentierung ermittelt (sprich: ein String erzeugt) und dieser String mit der bereits vorhandenen Funktion lcd_string ausgegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen &lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Beispiel&lt;br /&gt;
int variable = 42;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe des Zeichens dessen ASCII-Code gleich dem Variablenwert ist&lt;br /&gt;
    // (Im Beispiel entspricht der ASCII-Code 42 dem Zeichen *)&lt;br /&gt;
    // http://www.code-knacker.de/ascii.htm&lt;br /&gt;
    lcd_data(variable);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
 &lt;br /&gt;
    // Ausgabe der Variable als Text in dezimaler Schreibweise&lt;br /&gt;
    {&lt;br /&gt;
       // ... umwandeln siehe FAQ Artikel bei www.mikrocontroller.net&lt;br /&gt;
       // WinAVR hat eine itoa()-Funktion, das erfordert obiges #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
       char Buffer[20]; // in diesem {} lokal&lt;br /&gt;
       itoa( variable, Buffer, 10 ); &lt;br /&gt;
&lt;br /&gt;
       // ... ausgeben  &lt;br /&gt;
       lcd_string( Buffer );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einrichten eines Projekts muss man zu der Datei mit dem Hauptprogramm auch die Datei lcd-routines.c in das Projekt aufnehmen. Dies geschieht beim AVR Studio unter Source Files im Fenster AVR GCC oder bei WinAVR im Makefile (z.B. durch SRC += lcd-routines.c).&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVRs sind für viele Steuerungsaufgaben zu schnell. Wenn wir beispielsweise eine LED oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die CPU-Frequenz verwenden, da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, Vorgänge in Zeitabständen durchzuführen, die geringer als die Taktfrequenz des Controllers sind. Selbstverständlich sollte die resultierende Frequenz auch noch möglichst genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist ganz einfach ein bestimmtes Register im µC, das völlig ohne Zutun des Programms, also per Hardware, hochgezählt wird. Das alleine wäre noch nicht allzu nützlich, wenn nicht dieses Hardwareregister bei bestimmten Zählerständen einen Interrupt auslösen könnte. Ein solches Ereignis ist der Overflow: Da die Bitbreite des Registers beschränkt ist, kommt es natürlich auch vor, dass der Zähler so hoch zählt, dass der nächste Zählerstand mit dieser Bitbreite nicht mehr darstellbar ist und der Zähler wieder auf 0 zurückgesetzt wird. Dieses Ereignis nennt man den Overflow und es ist möglich an dieses Ereignis einen Interrupt zu koppeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Alternativvorschlag mthomas &lt;br /&gt;
Jeder Timer verfügt über ein Zählerregister im Mikrocontroller, das automatisch und ohne Zutun des Programms von der Hardware weitergezählt wird. In einem einfachen Anwendungsfall stellt man den Timer auf eine Zählgeschwindigkeit (Frequenz) und kann dann anhand des Zählerstands ermitteln, wie viel Zeit vergangen ist. Das eigentlich Nützliche an Timern ist jedoch, dass man  bestimmte Zählerstände mit Interrupts verknüpfen kann, so dass der Controller beim Auftreten automatisch eine vom Anwender geschriebene Routine aufruft. Eines dieser möglichen Ereignis ist der Overflow ((Zähler-)Überlauf), der dann auftritt, wenn der Wert des Zählerregisters (Timer/Counter-Register) den maximal möglichen Wert überschreitet. Der Maximalwert wird durch die Bitbreite des Zählerregisters bestimmt (z.B. 255 bei 8-Bit Timern). Beim Überlauf/Overflow wird der Zähler durch die Hardware auf 0 zurückgesetzt und die Zählung beginnt von neuem. Wurde vorher der Overflow-Interrupt für den Timer aktiviert (im Timer Control Register) unterbricht der Controller automatisch die Ausführung des Hauptprogramms und verzweigt in die Interrupt-Routine des Anwenders.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auf den AT90S2313. Für andere Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer Auflösung von 65536. Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz, der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
== Der Vorteiler (Prescaler) ==&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler dient dazu, den CPU-Takt vorerst um einen einstellbaren Faktor zu reduzieren. Die so geteilte Frequenz wird den Eingängen der Timer zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024 einstellen, wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca. 4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw. Zählregister (TCNTx) mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle verfügen über mindestens einen, teilweise sogar zwei, 8-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird z.B bei AT90S2313 über folgende Register angesprochen (bei anderen Typen weitestgehend analog):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    TCCR0 |= (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst und die daran gebundene Interrupt-Routine abgearbeitet. Das TOV Flag &lt;br /&gt;
lässt sich durch das Hineinschreiben einer 1 und nicht wie erwartet einer 0 wieder zurücksetzen.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen außer den 8-Bit Timern auch 16-Bit Timer. Die 16-Bit Timer/Counter sind etwas komplexer aufgebaut als die 8-Bit Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM13&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM12 (CTC1)&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorteiler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt, und C der voreingestellte Vergleichswert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin T1, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin T1, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T1 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T1 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0, bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;TCNT1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;OCR1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;OCR1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;OCR1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;OCR1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäß folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]] [[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Ausserdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr. Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen kleiner 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4us warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.6 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t x;&lt;br /&gt;
&lt;br /&gt;
    x = 10;&lt;br /&gt;
&lt;br /&gt;
    while (x &amp;gt;= 0)&lt;br /&gt;
    {&lt;br /&gt;
      // tu was&lt;br /&gt;
&lt;br /&gt;
      x--;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog? ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Einige Controller haben einen eigenen Watchdog Oszillator, z.B. der Tiny2313 mit 128kHz. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde, beginnt der Counter von 0 an hochzuzählen. &lt;br /&gt;
Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern, müssen wir den Watchdog regelmäßig wieder neu starten bzw. rücksetzen (Watchdog Reset). &lt;br /&gt;
Dies sollte innerhalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern, muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten. Es müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. &lt;br /&gt;
Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist, wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei &#039;&#039;wdt.h&#039;&#039; (&#039;&#039;#include &amp;lt;avr/wdt.h&amp;gt;&#039;&#039;) in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch gestartet wird. &lt;br /&gt;
Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. &lt;br /&gt;
Falls ein anderer Wert gewünscht ist, so kann dies im Makfile in den Linker-Optionen eingetragen werden. &lt;br /&gt;
Dazu muss in der Zeile LDFLAGS folgende Option angefügt werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert&lt;br /&gt;
:Mögliche Timeoutwerte:&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Konstante&lt;br /&gt;
! Wert&lt;br /&gt;
! TimeOut&lt;br /&gt;
|- &lt;br /&gt;
| WDTO_15MS   &lt;br /&gt;
| 0&lt;br /&gt;
| 15 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_30MS   &lt;br /&gt;
| 1&lt;br /&gt;
| 30 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_60MS   &lt;br /&gt;
| 2&lt;br /&gt;
| 60 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_120MS   &lt;br /&gt;
| 3&lt;br /&gt;
| 120 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_250MS   &lt;br /&gt;
| 4&lt;br /&gt;
| 250 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_500MS   &lt;br /&gt;
| 5&lt;br /&gt;
| 500 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_1S   &lt;br /&gt;
| 6&lt;br /&gt;
| 1 S&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_2S   &lt;br /&gt;
| 7&lt;br /&gt;
| 2s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
== Watchdog-Anwendungshinweise ==&lt;br /&gt;
&lt;br /&gt;
Ob nun der Watchdog als Schutzfunktion überhaupt verwendet werden soll, hängt stark von der Anwendung, der genutzten Peripherie und dem Umfang und der Qualitätssicherung des Codes ab. Will man sicher gehen, dass ein Programm sich nicht in einer Endlosschleife verfängt, ist der Wachdog das geeignete Mittel dies zu verhindern. Weiterhin kann bei geschickter Programmierung der Watchdog dazu genutzt werden, bestimmte Stromsparfunktionen zu implementieren. Bei einigen neueren AVRs (z.B. dem ATTiny13) kann der Watchdog auch direkt als Timer genutzt werden, der den Controller aus einem Schlafmodus aufweckt. Auch dies kann im &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register eingestellt werden. Außerdem bietet der WD die einzige Möglichkeit einen beabsichtigten System-Reset (ein &amp;quot;richtiger Reset&amp;quot;, kein &amp;quot;jmp 0x0000&amp;quot;) ohne externe Beschaltung auszulösen, was z.B. bei der Implementierung eines Bootloaders nützlich ist. Bei bestimmten Anwendungen kann die Nutzung des WD als &amp;quot;ultimative Deadlock-Sicherung für nicht bedachte Zustände&amp;quot; natürlich immer als zusätzliche Sicherung dienen. &lt;br /&gt;
&lt;br /&gt;
Es besteht die Möglichkeit herauszufinden, ob ein Reset durch den Watchdog ausgelöst wurde (beim ATmega16 z.B. Bit WDRF in MCUCSR). Diese Information sollte auch genutzt werden, falls ein WD-Reset in der Anwendung nicht planmäßig implementiert wurde. Zum Beispiel kann man eine LED an einen freien Pin hängen, die nur bei einem Reset durch den WD aufleuchtet oder aber das &amp;quot;Ereignis WD-Reset&amp;quot; im internen EEPROM des AVR absichern, um die Information später z.B. über UART oder ein Display auszugeben (oder einfach den EEPROM-Inhalt über die ISP/JTAG-Schnittstelle auslesen).&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/77273#642501 Bug in ATtiny2313?]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt der Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft. Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe Abschnitt (TODO: verlinken) im Anhang.&lt;br /&gt;
&amp;lt;!--Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. vgl. [[AVR-GCC-Tutorial#Anhang|Anhang]])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.B. &#039;&#039;void TIMER0_OVF_vect(void)...&#039;&#039;). Der Unterschied im Vergleich zu ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben.  &amp;lt;!--Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)), als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur &lt;br /&gt;
in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechnungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechnungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0, PORTA ist danach 0b00000001. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-oder-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8 (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten &#039;&#039;Sleep-Modes&#039;&#039; (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_enable()&#039;&#039;&#039;&lt;br /&gt;
:aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_cpu()&#039;&#039;&#039;&lt;br /&gt;
: Versetzt den Controller in den Schlafmodus (sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt)&lt;br /&gt;
* &#039;&#039;&#039;sleep_disable()&#039;&#039;&#039;&lt;br /&gt;
:deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts.&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;allozieren&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich, um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory, bei AVR Controllern mit mehr als 64kB Flash auch elpm). Die Standard-Laufzeitbibliothek des avr-gcc (die avr-libc) stellt diese Funktionen nach Einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Lokale Variablen (eigentlich Konstanten) innerhalb von Funktionen können ebenfalls im Programmspeicher abgelegt werden. Dazu ist bei der Definition jedoch ein &#039;&#039;static&#039;&#039; voranzustellen, da solche &amp;quot;Variablen&amp;quot; nicht auf dem Stack bzw. (bei Optimierung) in Registern verwaltet werden können. Der Compiler &amp;quot;wirft&amp;quot; eine Warnung falls static fehlt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray2 };&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  static /*const*/ uint8_t pgmTestByteLocal PROGMEM = 0x55;&lt;br /&gt;
  static /*const*/ char pgmTestStringLocal[] PROGMEM = &amp;quot;im Flash&amp;quot;;&lt;br /&gt;
  // so nicht (static fehlt): char pgmTestStringLocalFalsch [] PROGMEM = &amp;quot;so nicht&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 // ...&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
    //...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte....&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWort);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;groß&amp;quot; (Stand avr-gcc 3.4.x). Damit ist der mögliche Speicherbereich für &amp;quot;Flash-Konstanten&amp;quot; auf 64kB begrenzt.  &amp;lt;!-- Einige avr-libc/pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher) (intern via Assembler Anweisung ELPM). Die Initialisierungswerde des Speicherinhalts jenseits der 64kB-Marke müssen dann jedoch auf anderem Weg angelegt werden (nicht PROGMEM, evtl. eigene Section und Linker-Optionen - TODO) /// alt - und nicht ganz korrekt: (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.)--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray enthält nun die Startadresse des Byte-Arrays pgmFooByteArray1&lt;br /&gt;
    // Allerdings würde ein direkter Zugriff mit diesem Pointer (z.B. temp=*ptrToArray)&lt;br /&gt;
    // &#039;&#039;&#039;nicht&#039;&#039;&#039; den Inhalt von pgmFooByteArray1[0] liefern, sondern von einer Speicherstelle&lt;br /&gt;
    // im &#039;&#039;&#039;RAM&#039;&#039;&#039;, die die gleiche Adresse hat wie pgmFooByteArray1[0]&lt;br /&gt;
    // Daher muss nun die Funktion pgm_read_byte() benutzt werden, die die in ptrToArray&lt;br /&gt;
    // enthaltene Adresse benutzt und auf das Flash zugreift.&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray enthält nun die Adresse des ersten Elements des Byte-Arrays pgmFooByteArray2&lt;br /&gt;
    // da im zweiten Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmFooByteArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Array aus Zeichenketten im Flash-Speicher ===&lt;br /&gt;
&lt;br /&gt;
Felder aus Zeichenketten im Flash-Speicher werden in zwei Schritten angelegt: Zuerst die einzelnen Elemente des Arrays und im Anschluss ein Array, in dem die Addressen der Zeichenketten abgelegt werden. Zum Auslesen wird zuerst die Adresse des i-ten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, auf das Element (die Zeichenkette) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const char str1[] PROGMEM = &amp;quot;first_A&amp;quot;;&lt;br /&gt;
const char str2[] PROGMEM = &amp;quot;second_A&amp;quot;;&lt;br /&gt;
const char str3[] PROGMEM = &amp;quot;third_A&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char *strarray1[] PROGMEM = {&lt;br /&gt;
	str1,&lt;br /&gt;
	str2,&lt;br /&gt;
	str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	int i, j, l;&lt;br /&gt;
	const char *pstrflash;&lt;br /&gt;
	char work[20], work2[20];&lt;br /&gt;
	// fuer Simulation: per volatile Optimierung verhindern, &lt;br /&gt;
	//                  da c nicht genutzt&lt;br /&gt;
	volatile char c;&lt;br /&gt;
	&lt;br /&gt;
	for ( i = 0; i &amp;lt; (sizeof(strarray1)/sizeof(strarray1[0]) ); i++ ) {&lt;br /&gt;
&lt;br /&gt;
		// setze Pointer auf die Addresse des i-ten Elements des&lt;br /&gt;
		// &amp;quot;Flash-Arrays&amp;quot; (str1, str2, ...)&lt;br /&gt;
		pstrflash = (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) );&lt;br /&gt;
		&lt;br /&gt;
		// kopiere den Inhalt der Zeichenkette von der&lt;br /&gt;
		// in pstrflash abgelegten Adresse in das work-Array&lt;br /&gt;
		// analog zu strcpy( work, strarray1[i]) wenn alles im RAM&lt;br /&gt;
		strcpy_P( work, pstrflash );&lt;br /&gt;
		// verkuerzt:&lt;br /&gt;
		strcpy_P( work2, (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) ) );&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
		// Zeichen-fuer-Zeichen&lt;br /&gt;
		l = strlen_P( pstrflash );&lt;br /&gt;
		for ( j=0; j &amp;lt; l; j++ ) {&lt;br /&gt;
			// analog zu c=strarray[i][j] wenn alles im RAM&lt;br /&gt;
			c = (char)( pgm_read_byte( pstrflash++ ) );&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	while (1) { ; }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: &amp;quot;How do I put an array of strings completely in ROM?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5)) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Vorsicht: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char textImFlashOK[] PROGMEM = &amp;quot;mit[]&amp;quot;; &lt;br /&gt;
// = Daten im &amp;quot;Flash&amp;quot;, textImFlashOK* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char* textImFlashProblem PROGMEM = &amp;quot;mit*&amp;quot;;&lt;br /&gt;
// Konflikt: Daten im BSS (lies: RAM), textImFlashFAIL* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der Initialisierungsstring von &amp;quot;textImFlashProblem&amp;quot; zu den Konstanten ans Ende des Programmcodes gelegt wird (BSS), von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte (und wird). Da der lesende Code (mittels pgm_read*) trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein weiters Problem des AVR-GCC (gesehen bei avr-gcc 3.4.1 und 3.4.2) bei der Anpassung an die Harvard-Architektur zu sein (konstanter Pointer auf variable Daten?!). Abhilfe (&amp;quot;Workaround&amp;quot;): Initialisierung bei Zeichenketten mit [] oder gleich im Code PSTR(&amp;quot;...&amp;quot;) nutzen.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden, ob es sich um eine Adresse im Flash oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind.&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die einen im Flash abgelegten String z.B. an eine UART ausgibt, würde dann so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void uart_puts_p(const char *text)&lt;br /&gt;
{&lt;br /&gt;
    char Zeichen;&lt;br /&gt;
&lt;br /&gt;
    while (Zeichen = pgm_read_byte(text))&lt;br /&gt;
    {   /* so lange, wie mittels pgm_read_byte ein Zeichen vom Flash gelesen&lt;br /&gt;
           werden konnte, welches nicht das &amp;quot;String-Endezeichen&amp;quot; darstellt */&lt;br /&gt;
&lt;br /&gt;
        /* Das gelesene Zeichen über die normalen Kanäle verschicken */&lt;br /&gt;
        uart_putc(Zeichen);&lt;br /&gt;
        text++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Flash in der Anwendung schreiben ===&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option (auch bekannt als Bootloader-Support) können Teile des Flash-Speichers auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. Bei wenigen &amp;quot;kleinen&amp;quot; AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm(Flash)- und Datenspeicher(RAM) auf. Der C-Standard und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiß&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, dass vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgegeben sind, die innerhalb weniger Taktzyklen aufeinanderfolgen müssen (&amp;quot;timed sequence&amp;quot;). Auch dies muss bei Nutzung der Funktionen aus der avr-libc/eeprom.h-Datei nicht selbst implementiert werden. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Siehe dazu folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt; // wird in aktuellen Versionen der avr-lib mit xx.h eingebunden&lt;br /&gt;
&lt;br /&gt;
// EEMEM wird bei aktuellen Versionen der avr-lib in eeprom.h definiert&lt;br /&gt;
// hier: definiere falls noch nicht bekannt (&amp;quot;alte&amp;quot; avr-libc)&lt;br /&gt;
#ifndef EEMEM&lt;br /&gt;
// alle Textstellen EEMEM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEMEM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
//...&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // &#039;Variablen&#039; eeFooByte geschrieben&lt;br /&gt;
//...&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z.B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    #define EEPROM_DEF 0xFF&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ( ( myByte = eeprom_read_byte(&amp;amp;eeFooByte) ) == EEPROM_DEF ) {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
//...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprüft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und möglicherweise noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,3);&lt;br /&gt;
&lt;br /&gt;
    /* dito etwas anschaulicher aber &amp;quot;unnütze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fließkommazahlen lassen sich recht praktisch über eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t i[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    uint8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEMEM;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
//...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklaration abgeleiteten EEPROM-Inhalt in eine Datei geschrieben werden (übliche Dateiendung: .eep, Daten im Intel Hex-Format). Damit können recht elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen (siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles). Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden (Write EEPROM), wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse (vgl. Datenblatt Abschnitt Fuse Bits) die vorherigen Daten (EESAVE programmed = 0), deren Position möglicherweise nicht mehr mit der Belegung im aktuellen Programm übereinstimmt oder den Standardwert nach &amp;quot;Chip Erase&amp;quot;: 0xFF (EESAVE unprogrammed = 1). Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Eine besondere Funktion des avr-gcc ist, dass mit entsprechenden Optionen im Makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Variable auf feste Adressen legen ===&lt;br /&gt;
&lt;br /&gt;
Gleich zu Beginn möchte ich darauf hinweisen, dass dieses Verfahren nur ein Workaround ist, mit dem man das Problem der anscheinend &amp;quot;zufälligen&amp;quot; Verteilung&lt;br /&gt;
der EEPROM-Variablen durch den Compiler etwas in den Griff bekommen kann.&lt;br /&gt;
&lt;br /&gt;
Hilfreich kann dies vor allem dann sein, wenn man z.B. über einen Kommandointerpreter (o.ä. Funktionen) direkt bestimmte EEPROM-Adressen manipulieren möchte. Auch wenn man über einen JTAG-Adapter (mk I oder mkII) den Programmablauf manipulieren möchte, indem man die EEPROM-Werte direkt ändert, kann diese Technik hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Im folgenden nun zwei Sourcelistings mit einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.h&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#inlcude &amp;lt;avr/eeprom.h&amp;gt;     // Die EEPROM-Definitionen/Macros der avr-libc einbinden&lt;br /&gt;
&lt;br /&gt;
#define   EESIZE   512      // Maximale Größe des EEPROMS&lt;br /&gt;
&lt;br /&gt;
#define   EE_DUMMY   0x000  // Dummyelement (Adresse 0 sollte nicht genutzt werden)&lt;br /&gt;
#define   EE_VALUE1  0x001  // Eine Bytevariable  &lt;br /&gt;
#define   EE_WORD1L  0x002  // Eine Wordvariable (Lowbyte)&lt;br /&gt;
#define   EE_WORD1H  0x003  // Eine Wordvariable (Highbyte)&lt;br /&gt;
#define   EE_VALUE2  0x004  // Eine weitere Bytevariable&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den Macros &#039;&#039;&#039;#define EE_VALUE1&#039;&#039;&#039; legt man den Namen und die Adresse der&lt;br /&gt;
&#039;Variablen&#039; fest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Die Adressen sollten fortlaufend, zumindest aber aufsteigend sortiert sein! Ansonsten besteht die Gefahr, daß man sehr schnell ein Durcheinander im EEPROM Speicher veranstaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Für den Compiler sind das lediglich Speicher-Adressen, über die auf das EEPROM zugegriffen wird. Der Compiler sieht nichts davon als eine echte Variable an und stößt sich daher auch nicht daran, wenn 2 Makros mit der gleichen Speicheradresse, bzw. überlappenden Speicherbereichen definiert werden. Es liegt einzig und alleine in der Hand des Programmierers, hier keinen Fehler zu machen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
   [EE_DUMMY]   = 0x00,&lt;br /&gt;
   [EE_VALUE1]  = 0x05,&lt;br /&gt;
   [EE_WORD1L]  = 0x01,   &lt;br /&gt;
   [EE_WORD1H]  = 0x00,&lt;br /&gt;
   [EE_VALUE2]  = 0xFF&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch die Verwendung eines Array, welches das gesammte EEPROM umfasst, bleibt&lt;br /&gt;
dem Compiler nicht anderes übrig, als das Array so zu platzieren, dass Element 0&lt;br /&gt;
des Arrays der Adresse 0 des EEPROMs entspricht. (&#039;&#039;Ich hoffe nur, dass die Compilerbauer daran nichts ändern!&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Wie man in dem obigen Codelisting auch sehen kann, hat das Verfahren einen kleinen Haken. Variablen die größer sind als 1 Byte, müssen etwas umständlicher&lt;br /&gt;
definiert werden. Benötigt man keine Initialisierung durch das Programm (was der Normalfall sein dürfte), dann kann man das auch so machen:&lt;br /&gt;
&lt;br /&gt;
Möchte man im EEPROM hintereinander beispielsweise Variablen, mit den Namen &#039;&#039;&#039;Wert&#039;&#039;&#039;, &#039;&#039;&#039;Anzahl&#039;&#039;&#039;, &#039;&#039;&#039;Name&#039;&#039;&#039; und &#039;&#039;&#039;Wertigkeit&#039;&#039;&#039; definieren, wobei Wert und Wertigkeit 1 Byte belegen sollen, Anzahl als 1 Wort (also 2 Bytes) und Name mit 10 Bytes reserviert werden soll, so geht auch folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes Makro definiert also seine Startadresse durch die Startadresse der unmittelbar vorhergehende &#039;Variablen&#039; plus der Anzahl der Bytes die von der vorhergehenden &#039;Variablen&#039; verbraucht werden. Dadurch ist man zumindest etwas auf der sicheren Seite, dass keine 2 &#039;Variablen&#039; im EEPROM überlappend definiert werden. Möchte man eine weitere &#039;Variable&#039; hinzufügen, so wird deren&lt;br /&gt;
Name, einfach anstelle der EE_LAST eingesetzt und eine neue Zeile für EE_LAST eingefügt, in der dann die Größe der &#039;Variablen&#039; festgelegt wird. Zb.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_PROZENT    ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( double ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EE_PROZENT legt die Startadresse für eine neue &#039;Variable&#039; des Datentyps double fest.&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf die EEPROM Werte kann dann z.B.so erfolgen:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t   temp1;&lt;br /&gt;
uint16_t  temp2;&lt;br /&gt;
&lt;br /&gt;
temp1 = eeprom_read_byte(EE_VALUE1);&lt;br /&gt;
temp2 = eeprom_read_word(EE_WORD1L);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ob die in der avr-libc vorhandenen Funktionen dafür verwendet werden können, weiß ich nicht. Aber in einigen Fällen muss man sich sowieso eigene Funktionen&lt;br /&gt;
bauen, welche die spezifischen Anforderungen (Interrupt - Atom Problem, etc.)&lt;br /&gt;
erfüllen.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist nur eine Möglichkeit, wie man dies realisieren kann. Sie bietet einem eine relativ einfache Art die EEPROM-Werte&lt;br /&gt;
auf beliebige Adressen zu legen oder Adressen zu ändern. Die Andere Möglichkeit besteht darin, die EEPROM-Werte wie folgt zu belegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
  0x00,                     //  ee_dummy&lt;br /&gt;
  0x05,                     //  ee_value1&lt;br /&gt;
  0x01,                     //  ee_word1L&lt;br /&gt;
  0x00,                     // (ee_word1H)&lt;br /&gt;
  0xFF                      //  ee_value2&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei kann man Variablen, die größer sind als 1 Byte einfacher definieren und&lt;br /&gt;
man muss nur die Highbyte- oder Lowbyte-Adresse in der &amp;quot;eeprom.h&amp;quot; definieren.&lt;br /&gt;
Allerdings muss man hier höllisch aufpassen, dass man nicht um eine oder mehrere&lt;br /&gt;
Positionen verrutscht!&lt;br /&gt;
&lt;br /&gt;
Welche der beiden Möglichkeiten man einsetzt, hängt vor allem davon ab, wieviele&lt;br /&gt;
Byte, Word und sonstige Variablen man benutzt. Gewöhnen sollte man sich an beide Varianten können ;)&lt;br /&gt;
&lt;br /&gt;
Kleine Schlussbemerkung:&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc unterstützt die Variante 1 und die Variante 2&lt;br /&gt;
* Der icc-avr Compiler unterstützt nur die Variante 2!&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Bei alten Versionen der avr-libc wurden nicht alle AVR Controller  untersützt. Z.B. bei der avr-libc Version 1.2.3 insbesondere bei AVRs &amp;quot;der neuen Generation&amp;quot; (ATmega48/88/168/169) funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register). In neueren Versionen (z.B. avr-libc 1.4.3 aus WinAVR 20050125) wurde die Zahl der unterstüzten Controller deutlich erweitert und eine Methode zur leichten Anpassung an zukünftige Controller eingeführt.&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Will oder kann man nicht auf die neue Version aktualisieren, kann der dort gezeigte Code auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;, gegebenfalls Schutz vor Unterbrechnung/Interrupt ergänzen &#039;&#039;uint8_t sreg; sreg=SREG; cli(); [EEPROM-Code] ; SREG=sreg; return;&#039;&#039;, siehe Abschnitt Interrupts). Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== EEPROM Register ===&lt;br /&gt;
Um das EEPROM anzusteuern sind drei Register von Bedeutung.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEAR Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL da in einem 8 Bit Register keine 512 Adressen adressiert werden können&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEDR Hier werden die Daten eingetragen die geschrieben werden sollen bzw. es enthält die gelesenen Daten&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EECR Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TABLE BORDER=1&amp;gt;&lt;br /&gt;
&amp;lt;TR&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;Bit&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;1&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;0&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&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;Name&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&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;TD&amp;gt;-&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERIE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEMWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERE&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;Read/Write&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;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&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;Init Value&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;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;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;TD&amp;gt;0&amp;lt;/TD&amp;gt;&lt;br /&gt;
&amp;lt;/TR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/TABLE&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bedeutung der Bits:&amp;lt;/U&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4-7 nicht belegt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 3 (EERIE) - EEPROM Ready Interrupt Enable:&amp;lt;/U&amp;gt; Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7) wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0 wird kein Interrupt ausgelöst.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 2(EEMWE) - EEPROM Master Write Enable:&amp;lt;/U&amp;gt; Dieses Bit bestimmt, daß wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE =0 ist und EEWE = 1 gesetzt wird hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst.&lt;br /&gt;
Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 1 (EEWE) - EEPROM Write Enable:&amp;lt;/U&amp;gt; Dieses Bit löst den Schreibvorgang aus wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist wird dieses Bit automatisch wieder auf 0 gesetzt und sofern EERIE gesetzt ist ein Interrupt ausgelöst. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ein Schreibvorgang sieht typischerweise wie folgt aus:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. EEPROM Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Daten übergeben an EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1 &amp;lt;BR&amp;gt;&lt;br /&gt;
5. (Optinal) Warten bis Schreibvorgang abgeschlossen ist.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 0 EERE – EEPROM Read Enable:&amp;lt;/U&amp;gt; Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit wenn das Bit EEWE=0 ist. ISt der LEsevorgang abgeschlossen wird das Bit wieder auf 0 gesetzt und das EEPROM ist für neue Lese/Schreibbefehle wieder bereit.&amp;lt;BR&amp;gt;&lt;br /&gt;
Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. Bereitschaft zum lesen prüfen (EEWE=0)&amp;lt;BR&amp;gt;&lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Lesezyklus auslösen mit EERE = 1&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Warten bis Lesevorgang abgeschlossen EERE = 0&amp;lt;BR&amp;gt;&lt;br /&gt;
5. Daten abholen aus EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bietet sich printf/sprintf an. &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t s[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( s, &amp;quot;Zählerstand: %d&amp;quot;, value );&lt;br /&gt;
    uart_puts( s );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, die Ausgabe stdout auf eigene Ausgabefunktionen umzuleiten. Dazu wird dem Ausgabemechanismus eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben (wie auch immer die Ausgabe stattfindet). Alle anderen, höheren, Funktionen greifen letztendlich auf diese eine Ausgabefunktion zurück, die die Ausgabe eines einzelnen Zeichens vornimmt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uart_init();&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
    printf( &amp;quot;Hello, world!\n&amp;quot; );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil aller printf-Varianten: Sie sind recht speicherintensiv.&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C- und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gnu-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Inline-Assembler: Die Assembleranweisungen werden direkt in den C-Code integriert. Eine Quellcode-Datei enthält somit C- und Assembleranweisungen&lt;br /&gt;
* Assembler-Dateien: Der Assemblercode befindet sich in eigenen Quellcodedateien. Diese werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung (NOP steht für No Operation). Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=Warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
   /* Verzögern der weiteren Programmausführung um&lt;br /&gt;
      genau 3 Taktzyklen */&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeintlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
&lt;br /&gt;
    /* leere Schleife - wird bei eingeschalteter Compiler-Optimierung   wegoptimiert */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* alternative Methode (keine Optimierung): */&lt;br /&gt;
    volatile uint16_t j;&lt;br /&gt;
    for (j = 0; j &amp;lt; 1000; j++)&lt;br /&gt;
      ;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützlicher &amp;quot;Assembler-Einzeiler&amp;quot; ist der Aufruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;);&#039;&#039;), da hierzu in älteren Versionen der avr-libc keine eigene Funktion existiert (in neueren Versionen &#039;&#039;sleep_cpu()&#039;&#039; aus sleep.h).&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise Delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil, dass die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static inline void delayloop16 (uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
    asm volatile (&amp;quot;cp  %A0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;cpc %B0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;breq 2f               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;1:                    \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;sbiw %0,1             \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;brne 1b               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;2:                    &amp;quot;  &lt;br /&gt;
                  : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
	          : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
    );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Anweisung wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen. Der Zeilenumbruch teilt dem Assembler mit, dass ein neuer Befehl beginnt.&lt;br /&gt;
* Als Sprung-Marken (Labels) werden Ziffern verwendet. Diese speziellen Labels sind mehrfach im Code verwendbar. Gesprungen wird jeweils zurück (b) oder vorwärts (f) zum nächsten ausffindbaren Label.&lt;br /&gt;
&lt;br /&gt;
Das Resultat zeigt ein Blick in die Assembler-Datei, die der Compiler mit der option &amp;lt;tt&amp;gt;-save-temps&amp;lt;/tt&amp;gt; nicht löscht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	cp  r24, __zero_reg__ 	 ;  count&lt;br /&gt;
	cpc r25, __zero_reg__ 	 ;  count&lt;br /&gt;
	breq 2f               &lt;br /&gt;
	1:                    &lt;br /&gt;
	sbiw r24,1             	 ;  count&lt;br /&gt;
	brne 1b               &lt;br /&gt;
	2:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc im Abschnitt [http://www.nongnu.org/avr-libc/user-manual/inline_asm.html Related Pages/Inline Asm]. &lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc Deutsche Einführung in Inline-Assembler]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
Wenn man mit dem AVR Studio arbeitet, kann alternativ auch das standardmäßig erstellte Makefile bearbeitet und folgende Zeilen eingefügt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
## Objects that must be built in order to link&lt;br /&gt;
OBJECTS = (alte Dateien...) useful.o&lt;br /&gt;
&lt;br /&gt;
## Compile&lt;br /&gt;
## Hier folgt eine Liste der gelinkten Dateien, darunter einfügen:&lt;br /&gt;
useful.o: ../useful.S&lt;br /&gt;
	$(CC) $(INCLUDES) $(ASMFLAGS) -c  $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Das war es schon. Allerdings gilt es zu beachten, dass das makefile über &amp;quot;Project -&amp;gt; Configuration options&amp;quot; selbst einzubinden ist, sonst wird es natürlich wieder überschrieben.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel eine Funktion &#039;&#039;superFunc&#039;&#039;, die alle Pins des Ports D auf &amp;quot;Ausgang&amp;quot; schaltet, eine Funktion &#039;&#039;ultraFunc&#039;&#039;, die die Ausgänge entsprechend des übergebenen Parameters schaltet, eine Funktion &#039;&#039;gigaFunc&#039;&#039;, die den Status von Port A zurückgibt und eine Funktion &#039;&#039;addFunc&#039;&#039;, die zwei Bytes zu einem 16-bit-Wort addiert. Die Zuweisungen im C-Code (PORTx = ...) verhindern, dass der Compiler die Aufrufe wegoptimiert und dienen nur zur Veranschaulichung der Parameterübergaben.&lt;br /&gt;
&lt;br /&gt;
Zuerst der Assembler-Code. Der Dateiname sei useful.S:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//; Arbeitsregister (ohne &amp;quot;r&amp;quot;) &lt;br /&gt;
workreg  = 16&lt;br /&gt;
workreg2 = 17&lt;br /&gt;
&lt;br /&gt;
//; Konstante:&lt;br /&gt;
ALLOUT = 0xff&lt;br /&gt;
&lt;br /&gt;
//; ** Setze alle Pins von PortD auf Ausgang **&lt;br /&gt;
//; keine Parameter, keine Rückgabe&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg  // beachte: _SFR_IO_ADDR()&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Setze PORTD auf übergebenen Wert **&lt;br /&gt;
//; Parameter in r24 (LSB immer bei &amp;quot;graden&amp;quot; Nummern)&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zustand von PINA zurückgeben **&lt;br /&gt;
//; Rückgabewerte in r24:r25 (LSB:MSB), hier nur LSB genutzt&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zwei Bytes addieren und 16-bit-Wort zurückgeben **&lt;br /&gt;
//; Parameter in r24 (Summand1) und r22 (Summand2) -&lt;br /&gt;
//;  Parameter sind Word-&amp;quot;aligned&amp;quot; d.h. LSB immer auf &amp;quot;graden&amp;quot;&lt;br /&gt;
//;  Registernummern. Bei 8-Bit und 16-Bit Paramtern somit &lt;br /&gt;
//;  beginnend bei r24 dann r22 dann r20 etc.&lt;br /&gt;
//; Rückgabewert in r24:r25&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   push workreg2&lt;br /&gt;
   clr workreg2&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
   pop workreg2&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
//; oh je - sorry - Mein AVR-Assembler ist eingerostet, hoffe das stimmt so...&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Makefile ist der Name der Assembler-Quellcodedatei einzutragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
ASRC = useful.S&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Aufruf erfolgt dann im C-Code so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
extern void superFunc(void);&lt;br /&gt;
extern void ultraFunc(uint8_t setVal);&lt;br /&gt;
extern uint8_t gigaFunc(void);&lt;br /&gt;
extern uint16_t addFunc(uint8_t w1, uint8_t w2);&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
[...]&lt;br /&gt;
  superFunc();&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
&lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
[...]&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird wieder in der lss-Datei ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
   superFunc();&lt;br /&gt;
 148:	0e 94 f6 00 	call	0x1ec&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
 14c:	85 e5       	ldi	r24, 0x55	; 85&lt;br /&gt;
 14e:	0e 94 fb 00 	call	0x1f6&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
 152:	0e 94 fd 00 	call	0x1fa&lt;br /&gt;
 156:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
  &lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
 158:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 15a:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 15c:	0e 94 ff 00 	call	0x1fe&lt;br /&gt;
 160:	8b bb       	out	0x1b, r24	; 27&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
 162:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 164:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 166:	0e 94 fc 00 	call	0x1f8&lt;br /&gt;
 16a:	89 2f       	mov	r24, r25&lt;br /&gt;
 16c:	99 27       	eor	r25, r25&lt;br /&gt;
 16e:	88 bb       	out	0x18, r24	; 24&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
000001ec &amp;lt;superFunc&amp;gt;:&lt;br /&gt;
// setze alle Pins von PortD auf Ausgang&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1ec:	0f 93       	push	r16&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
 1ee:	0f ef       	ldi	r16, 0xFF	; 255&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg&lt;br /&gt;
 1f0:	01 bb       	out	0x11, r16	; 17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 1f2:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 1f4:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001f6 &amp;lt;ultraFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// setze PORTD auf übergebenen Wert&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
 1f6:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
   ret&lt;br /&gt;
 1f8:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fa &amp;lt;gigaFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Zustand von PINA zurückgeben&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
 1fa:	89 b3       	in	r24, 0x19	; 25&lt;br /&gt;
   ret&lt;br /&gt;
 1fc:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fe &amp;lt;addFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// zwei Bytes addieren und 16-bit-Wort zurückgeben&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1fe:	0f 93       	push	r16&lt;br /&gt;
   push workreg2&lt;br /&gt;
 200:	1f 93       	push	r17&lt;br /&gt;
   clr workreg2&lt;br /&gt;
 202:	11 27       	eor	r17, r17&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
 204:	06 2f       	mov	r16, r22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
 206:	08 0f       	add	r16, r24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
 208:	11 1d       	adc	r17, r1&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
 20a:	c8 01       	movw	r24, r16&lt;br /&gt;
   pop workreg2&lt;br /&gt;
 20c:	1f 91       	pop	r17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 20e:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 210:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zuweisung von Registern zu Parameternummer und die Register für die Rückgabewerte sind in den &amp;quot;Register Usage Guidelines&amp;quot; der avr-libc-Dokumentation erläutert.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/assembler.html avr-libc-Dokumentation: Related Pages/avr-libc and assembler programs]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage avr-libc-Dokumentation: Related Pages/FAQ/&amp;quot;What registers are used by the C compiler?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
== Globale Variablen für Datenaustausch ==&lt;br /&gt;
&lt;br /&gt;
Oftmals kommt man um globale Variablen nicht herum, z.B. um den Datenaustausch zwischen Hauptprogramm und Interrupt-Routinen zu realisieren. &lt;br /&gt;
Hierzu muss man im Assembler wissen, wo genau die Variable vom C-Compiler abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
Hierzu muss die Variable, hier &amp;quot;zaehler&amp;quot; genannt, zuerst im C-Code als Global definiert werden, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
int16_t main (void)&lt;br /&gt;
{&lt;br /&gt;
    // irgendein Code, in dem zaehler benutzt werden kann&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im foldenden Assembler-Beispiel wird der Externe Interrupt0  verwendet, um den Zähler hochzuzählen. Es fehlen die Initialisierungen des Interrupts und die Interrupt-Freigabe, so richtig sinnvoll ist der Code auch nicht, aber er zeigt (hoffentlich) wie es geht.&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit Interrupt-Vektoren gilt beim GCC-Assembler das Gleiche, wie bei C: Man muss die exakte Schreibweise beachten, ansonsten wird nicht der Interrupt-Vektor angelegt, sondern eine neue Funktion - und man wundert sich, dass nichts funktionert (vgl. das AVR-GCC-Handbuch).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
.extern zaehler&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      //; wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     //; Status-Register (SREG) sichern!&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     lds temp,zaehler               //; Wert aus dem Speicher lesen&lt;br /&gt;
     inc temp                       //; bearbeiten&lt;br /&gt;
     sts zaehler,temp               //; und wieder zurückschreiben&lt;br /&gt;
&lt;br /&gt;
     pop temp                       //; die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen im Assemblerfile anlegen ===&lt;br /&gt;
&lt;br /&gt;
Alternativ können Variablen aber auch im Assemblerfile angelegt werden. Dadurch kann auf eine .c-Datei verzichtet werden. Für das obige Beispiel könnte der Quelltext dann die Dateien zaehl_asm.S und zaehl_asm.h abgelegt werden, so dass nur noch zaehl_asm.S mit kompiliert werden müsste.&lt;br /&gt;
&lt;br /&gt;
Anstatt im Assemblerfile über das Schlüsselwort &#039;&#039;.extern &#039;&#039; auf eine vorhandene Variable zu verweisen, wird dazu mit dem Schlüsselwort &#039;&#039;.comm&#039;&#039; die benötigte Anzahl von Bytes für eine Variable reserviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.S&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
//; 1 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 1&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Headerdatei wird dann auf die Variable nur noch verwiesen (Schlüsselwort &#039;&#039;extern&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef ZAEHL_ASM_H&lt;br /&gt;
#define ZAEHL_ASM_H&lt;br /&gt;
&lt;br /&gt;
extern volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu globalen Variablen in C werden so angelegte Variablen nicht automatisch mit dem Wert 0 initialisiert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer als 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Variablen, die größer als &#039;&#039;&#039;ein&#039;&#039;&#039; Byte sind, können in Assembler auf ähnliche Art angesprochen werden. Hierzu müssen nur genug Bytes angefordert werden, um die Variable aufzunehmen. Soll z.B. für den Zähler eine Variable vom Typ &#039;&#039;unsigned long&#039;&#039;, also &#039;&#039;uint32_t&#039;&#039; verwendet werden, so müssen 4 Bytes reserviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die dazugehörige Deklaration im Headerfile wäre dann:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
extern volatile uint32_t zaehler;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen, die größer als ein Byte sind, werden die Werte beginnend mit dem niederwertigsten Byte im RAM abgelegt. Das folgende Codeschnippsel zeigt, wie unter Assembler auf die einzelnen Bytes zugegriffen werden kann. Dazu wird im Interrupt nun ein 32-Bit Zähler erhöht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      // wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     // Status-Register (SREG) sichern !&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     // 32-Bit-Zähler incrementieren&lt;br /&gt;
     lds temp, (zaehler + 0)        // 0. Byte (niederwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 0), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
     lds temp, (zaehler + 1)        // 1. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 1), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 2)        // 2. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 2), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 3)        // 3. Byte (höchstwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 3), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
RAUS:&lt;br /&gt;
     pop temp                       // die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TODO:&#039;&#039;&#039; 16-Bit / 32-Bit Variablen, Zugriff auf Arrays (Strings)&lt;br /&gt;
&lt;br /&gt;
= Anhang =&lt;br /&gt;
&lt;br /&gt;
== Besonderheiten bei der Anpassung bestehenden Quellcodes ==&lt;br /&gt;
&lt;br /&gt;
Einige Funktionen, die in frühren Versionen der avr-libc vorhanden waren, werden inzwischen als veraltet angesehen. Sie sind nicht mehr vorhanden oder als &#039;&#039;deprecated&#039;&#039; (missbilligt) ausgewiesen und Definitionen in &amp;lt;compat/deprecated.h&amp;gt; verschoben. Es empfiehlt sich, vorhandenen Code zu portieren und die alten Funktionen nicht mehr zu nutzen, auch wenn diese noch zur Verfügung stehen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zur Deklaration von Interrupt-Routinen ===&lt;br /&gt;
&lt;br /&gt;
Die Funktionen (eigentlich Makros) &#039;&#039;SIGNAL&#039;&#039; und &#039;&#039;INTERRUPT&#039;&#039; zur Deklaration von Interruptroutinen sollten nicht mehr genutzt werden. &lt;br /&gt;
&lt;br /&gt;
In aktuellen Versionen der avr-libc (z.B. avr-libc 1.4.3 aus WinAVR 20060125) werden Interruptroutinen, die &#039;&#039;&#039;nicht&#039;&#039;&#039; durch andere Interrupts &#039;&#039;&#039;unterbrechbar&#039;&#039;&#039; sind, mit ISR deklariert (siehe Abschnitt im Hauptteil). Auch die Benennung wurden vereinheitlicht und an die üblichen Bezeichnungen in den AVR Datenblättern angepasst. In der Dokumentation der avr-libc sind alte und neue Bezeichnungen in der Tabelle gegenübergestellt. Die erforderlichen Schritte zur Portierung:&lt;br /&gt;
&lt;br /&gt;
* #include von avr/signal.h entfernen&lt;br /&gt;
* SIGNAL duch ISR ersetzen&lt;br /&gt;
* Name des Interrupt-Vektors anpassen (SIG_* durch entsprechendes *_vect)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für die Anpassung zuerst ein &amp;quot;alter&amp;quot; Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer2 Output Compare bei einem ATmega8 */&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE2)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt wird der Vektor mit TIMER2 COMP bezeichnet. Die Bezeichnung in der avr-libc entspricht dem Namen im Datenblatt, Leerzeichen werden durch Unterstriche (_) ersetzt und ein _vect angehängt. &lt;br /&gt;
&lt;br /&gt;
Der neue Code sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt; &lt;br /&gt;
/* signal.h entfällt */&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER2_COMP_vect)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Unklarheiten bezüglich der neuen Vektorlabels hilft (noch) ein Blick in die Headerdatei des entsprechenden Controllers. Für das vorherige Beispiel also der Blick in die Datei iom8.h für den ATmega8, dort findet man die veraltete Bezeichnung unterhalb der aktuellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect		_VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2		_VECTOR(3)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Nachfolgendes mit avr-libc 1.4.5 (in WinAVR 1/2007 behoben - noch eine Weile auskommentiert lassen und dann löschen.&lt;br /&gt;
Konnte in alten Versionen signal.h ohne interrupt.h eingebunden werden, erhält man bei Verwendung der avr-libc Version 1.4.3 (WinAVR 2/2005) beim Compilieren eine Fehlermeldung, da mit signal.h nicht die erforderlichen Definitionen eingebunden werden. Der Lösungsvorschlag in signal.h auch interrupt.h einzubinden, wurde von den avr-libc-Enwicklern akzeptiert und das Problem ist  im Quellcode (CVS) bereits behoben. Es ist aber noch keine avr-libc-&amp;quot;Release&amp;quot; bzw. noch kein WinAVR mit dieser avr-libc-Korrektur verfügbar (Stand 5.2.2006). Will oder kann man den Quellcode nicht aktualisieren, gibt es folgende Alternativen:&lt;br /&gt;
* in Quellcodedateien, in denen nur avr/signal.h eingebunden wird, interrupt.h einbinden (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;). signal.h weiterhin einbinden, falls Kompatibiltät mit alten Versionen gewünscht ist.&lt;br /&gt;
* in der Datei signal.h (bein WinAVR in c:/WinAVR/avr/include/avr/signal.h) ein (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;) ergänzen --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;unterbrechbare&#039;&#039;&#039; Interruptroutinen, die mittels &#039;&#039;INTERRUPT&#039;&#039; deklariert sind, gibt es keinen direkten Ersatz in Form eines Makros. Solche Routinen sind laut Dokumentation der avr-libc in folgender Form zu deklarieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* ** alt ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
INTERRUPT(SIG_OVERFLOW0)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ** neu: ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void TIMER0_OVF_vect(void) __attribute__((interrupt));&lt;br /&gt;
void TIMER0_OVF_vect(void) &lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von &#039;&#039;INTERRUPT&#039;&#039; die Header-Datei &#039;&#039;compat/deprecated.h&#039;&#039; einzubinden. Man sollte bei dieser Gelegenheit jedoch nochmals überprüfen, ob die Funktionalität von &#039;&#039;INTERRUPT&#039;&#039; tatsächlich gewollt ist. In vielen Fällen wurde &#039;&#039;INTERRUPT&#039;&#039; dort genutzt, wo eigentlich &#039;&#039;SIGNAL&#039;&#039; (nunmehr &#039;&#039;ISR&#039;&#039;) hätte genutzt werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;inp&#039;&#039; und &#039;&#039;outp&#039;&#039; zum Einlesen bzw. Schreiben von Registern sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
unsigned char i, j;&lt;br /&gt;
&lt;br /&gt;
// alt:&lt;br /&gt;
  i = inp(PINA);&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  outp(PORTB, j);&lt;br /&gt;
&lt;br /&gt;
// neu (nicht mehr wirklich neu...):&lt;br /&gt;
  i = PINA&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  PORTB = j;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von inp und outp die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Zugriff auf Bits in Registern ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;cbi&#039;&#039; und &#039;&#039;sbi&#039;&#039; zum Löschen und Setzen von Bits sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg. Die Bezeichnung ist ohnehin irreführend da die Funktionen nur für Register mit Adressen im unteren Speicherbereich tatsächlich in die Assembleranweisungen cbi und sbi übersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// alt:&lt;br /&gt;
  sbi(PORTB, PB2);&lt;br /&gt;
  cbi(PORTC, PC1);&lt;br /&gt;
&lt;br /&gt;
// neu (auch nicht mehr wirklich neu...):&lt;br /&gt;
  PORTB |=  (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
  PORTC &amp;amp;= ~(1&amp;lt;&amp;lt;PC1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von sbi und cbi die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden. Wer unbedingt will, kann sich natürlich eigene Makros mit aussagekräftigeren Namen definieren. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &amp;amp;= ~(1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Selbstdefinierte (nicht-standardisierte) ganzzahlige Datentypen ===&lt;br /&gt;
&lt;br /&gt;
Bei den im Folgenden genannten Typdefinitionen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich, bei der Überarbeitung von altem Code die im Abschnitt &#039;&#039;standardisierten ganzzahligen Datentypen&#039;&#039; beschriebenen Datentypen zu nutzen (stdint.h) und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef unsigned char      BYTE;       // besser: uint8_t  aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned short     WORD;       // besser: uint16_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long      DWORD;      // besser: uint32_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long long QWORD;      // besser: uint64_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; BYTE : Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 255. &lt;br /&gt;
&lt;br /&gt;
; WORD : Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 65535. &lt;br /&gt;
&lt;br /&gt;
; DWORD : Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
; QWORD : Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Funktionen im Makefile ==&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken (Libraries/.a-Dateien) hinzufügen ===&lt;br /&gt;
&lt;br /&gt;
Um Funktionen aus Bibliotheken (&amp;quot;echte&amp;quot; Libraries, *.a-Dateien) zu nutzen, sind dem Linker die Namen der Bibliotheken als Parameter zu übergeben. Dazu ist die Option -l (kleines L) vorgesehen, an die der Name der Library angehängt wird. &lt;br /&gt;
&lt;br /&gt;
Dabei ist zu beachten, dass der Name der Library und der Dateiname der Library nicht identisch sind. Der hinter -l angegebene Name entspricht dem Dateinamen der Library ohne die Zeichenfolge &#039;&#039;lib&#039;&#039; am Anfang des Dateinamens und ohne die Endung &#039;&#039;.a&#039;&#039;. Sollen z.B. Funktionen aus einer Library mit dem Dateinamen &#039;&#039;libefsl.a&#039;&#039; eingebunden (gelinkt) werden, lautet der entsprechende Parameter -lefsl (vergl. auch -lm zum Anbinden von libm.a). &lt;br /&gt;
&lt;br /&gt;
In Makefiles wird traditonell eine make-Variable LDLIBS genutzt, in die &amp;quot;l-Parameter&amp;quot; abgelegt werden. Die WinAVR-makefile-Vorlage enthält diese Variable zwar nicht, dies stellt jedoch keine Einschränkung dar, da alle in der make-Variable LDFLAGS abgelegten Parameter an den Linker weitergereicht werden. &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Einbinden von Funktionen aus einer Library efsl (Dateiname libefsl.a)&lt;br /&gt;
LDFLAGS += -lefsl&lt;br /&gt;
# Einbinden von Funktionen aus einer Library xyz (Dateiname libxyz.a)&lt;br /&gt;
LDFLAGS += -lxyz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Liegen die Library-Dateien nicht im Standard Library-Suchpfad, sind die Pfade mittels Parameter &#039;&#039;-L&#039;&#039; ebenfalls anzugeben. (Der vordefinierte Suchpfad kann mittels &#039;&#039;avr-gcc --print-search-dirs&#039;&#039; angezeigt werden.)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Projekt (&amp;quot;superapp2&amp;quot;), in dem der Quellcode von zwei Libraries (efsl und xyz) und der Quellcode der eigentlichen Anwendung in verschiedenen Verzeichnissen mit der folgenden &amp;quot;Baumstruktur&amp;quot; abgelegt sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
superapp2&lt;br /&gt;
|&lt;br /&gt;
+----- efslsource (darin libefsl.a)&lt;br /&gt;
|&lt;br /&gt;
+----- xyzsource (darin libxyz.a)&lt;br /&gt;
|&lt;br /&gt;
+----- firmware (darin Anwendungs-Quellcode und Makefile)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt, dass im Makefile die Verzeichnis efslsource und xyzsource in den Library-Suchpfad aufzunehmen sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
LDFLAGS += -L../efslsource/ -L../xyzsource/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fuse-Bits ===&lt;br /&gt;
&lt;br /&gt;
Zur Berechnung der Fuse-Bits bietet sich neben dem Studium des Datenblattes auch der [http://palmavr.sourceforge.net/cgi-bin/fc.cgi AVR Fuse Calculator] an. Gewarnt werden muss vor der Benutzung von PonyProg, weil dort durch die negierte Darstellung gern Fehler gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Soll die Programmierung von Fuse- und Lockbits automatisiert werden, kann man dies ebenfalls durch Einträge im Makefile vornehmen, die beim Aufruf von &amp;quot;make program&amp;quot; an die genutzte Programmiersoftware übergeben werden. In der makefile-Vorlage von WinAVR (und mfile) gibt es dafuer jedoch keine &amp;quot;Ausfüllhilfe&amp;quot; (Stand 9/2006). Die folgenden Ausführungen gelten für die Programmiersoftware [[AVRDUDE]] (Standard in der WinAVR-Vorlage), können jedoch sinngemäß auf andere Programmiersoftware übertragen werden, die die Angabe der Fuse- und Lockbits-Einstellungen per Kommandozeilenparameter unterstützt (z.B. stk500.exe). Im einfachsten Fall ergänzt man im Makefile einige Variablen, deren Werte natürlich vom verwendeten Controller und den gewünschten Einstellungen abhängen (vgl. Datenblatt Fuse-/Lockbits):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#---------------- Programming Options (avrdude) ----------------&lt;br /&gt;
&lt;br /&gt;
#...&lt;br /&gt;
#Beispiel! f. ATmega16 - nicht einfach uebernehmen! Zahlenwerte anhand&lt;br /&gt;
#--------- des Datenblatts nachvollziehen und gegebenenfalls aendern.&lt;br /&gt;
#&lt;br /&gt;
AVRDUDE_WRITE_LFUSE = -U lfuse:w:0xff:m&lt;br /&gt;
AVRDUDE_WRITE_HFUSE = -U hfuse:w:0xd8:m&lt;br /&gt;
AVRDUDE_WRITE_LOCK  = -U lock:w:0x2f:m&lt;br /&gt;
#...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit diese Variablen auch genutzt werden, ist der Aufruf von avrdude im Makefile entsprechend zu ergänzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Program the device.  &lt;br /&gt;
program: $(TARGET).hex $(TARGET).eep&lt;br /&gt;
# ohne Fuse-/Lock-Einstellungen (nach WinAVR Vorlage Stand 4/2006)&lt;br /&gt;
#	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
#        $(AVRDUDE_WRITE_EEPROM)&lt;br /&gt;
# mit Fuse-/Lock-Einstellungen&lt;br /&gt;
        $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_LFUSE) \&lt;br /&gt;
        $(AVRDUDE_WRITE_HFUSE) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
        $(AVRDUDE_WRITE_EEPROM) $(AVRDUDE_WRITE_LOCK)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weiter Möglichkeit besteht darin, die Fuse- und Lockbit-Einstellungen vom Preprozessor/Compiler generieren zu lassen. Die Fuse-Bits werden dann bei Verwendung von AVRDUDE in eigene Hex-Files geschrieben. Hierzu kann man z.B. folgendes Konstrukt verwenden:&lt;br /&gt;
&lt;br /&gt;
In eine der C-Sourcen wird eine Variable je Fuse-Byte vom Typ &#039;&#039;unsigned char&#039;&#039; deklariert und in eine extra Section gepackt. Dies kann entweder in einem vorhandenen File passieren oder in ein neues (z.B. fuses.c) geschrieben werden. Das File muss im Makefile aber auf jeden Fall mit kompiliert und gelinkt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// tiny 2313 fuses low byte&lt;br /&gt;
#define CKDIV8  7&lt;br /&gt;
#define CKOUT   6&lt;br /&gt;
#define SUT1    5&lt;br /&gt;
#define SUT0    4&lt;br /&gt;
#define CKSEL3  3&lt;br /&gt;
#define CKSEL2  2&lt;br /&gt;
#define CKSEL1  1&lt;br /&gt;
#define CKSEL0  0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses high byte&lt;br /&gt;
#define DWEN       7&lt;br /&gt;
#define EESAVE     6&lt;br /&gt;
#define SPIEN      5&lt;br /&gt;
#define WDTON      4&lt;br /&gt;
#define BODLEVEL2  3&lt;br /&gt;
#define BODLEVEL1  2&lt;br /&gt;
#define BODLEVEL0  1&lt;br /&gt;
#define RSTDISBL   0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses extended byte&lt;br /&gt;
#define SELFPRGEN  0&lt;br /&gt;
&lt;br /&gt;
#define LFUSE         __attribute__ ((section (&amp;quot;lfuses&amp;quot;)))&lt;br /&gt;
#define HFUSE         __attribute__ ((section (&amp;quot;hfuses&amp;quot;)))&lt;br /&gt;
#define EFUSE         __attribute__ ((section (&amp;quot;efuses&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// select ext crystal 3-8Mhz&lt;br /&gt;
unsigned char lfuse LFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;CKDIV8) | (1&amp;lt;&amp;lt;CKOUT) | (1&amp;lt;&amp;lt;CKSEL3) | (1&amp;lt;&amp;lt;CKSEL2) | &lt;br /&gt;
      (0&amp;lt;&amp;lt;CKSEL1) | (1&amp;lt;&amp;lt;CKSEL0) | (0&amp;lt;&amp;lt;SUT1) | (1&amp;lt;&amp;lt;SUT0) );&lt;br /&gt;
unsigned char hfuse HFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;DWEN) | (1&amp;lt;&amp;lt;EESAVE) | (0&amp;lt;&amp;lt;SPIEN) | (1&amp;lt;&amp;lt;WDTON) | &lt;br /&gt;
      (1&amp;lt;&amp;lt;BODLEVEL2) | (1&amp;lt;&amp;lt;BODLEVEL1) | (0&amp;lt;&amp;lt;BODLEVEL0) | (1&amp;lt;&amp;lt;RSTDISBL) );&lt;br /&gt;
unsigned char efuse EFUSE =&lt;br /&gt;
    ((0&amp;lt;&amp;lt;SELFPRGEN));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG: Die Bitpositionen wurden nicht vollständig getestet!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Eine &amp;quot;1&amp;quot; bedeutet hier, dass das Fuse-Bit &#039;&#039;nicht&#039;&#039; programmiert wird - die Funktion also i.A. nicht aktiviert ist. Eine &amp;quot;0&amp;quot; hingegen aktiviert die meisten Funktionen. Dies ist wie im Datenblatt (1 = unprogrammed, 0 = programmed).&lt;br /&gt;
&lt;br /&gt;
Das Makefile muss nun noch um folgende Targets erweitert werden (mit Tabulator einrücken - nicht mit Leerzeichen):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
lfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j lfuses --change-section-address lfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-lfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-lfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U lfuse:w:$(TARGET)-lfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
hfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j hfuses --change-section-address hfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-hfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-hfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U hfuse:w:$(TARGET)-hfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
efuses: build&lt;br /&gt;
        -$(OBJCOPY) -j efuses --change-section-address efuses=0 \&lt;br /&gt;
         -O ihex $(TARGET).elf $(TARGET)-efuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-efuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U efuse:w:$(TARGET)-efuse.hex;&lt;br /&gt;
        fi;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Target &amp;quot;clean&amp;quot; muss noch um die Zeilen&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
        $(REMOVE) $(TARGET)-lfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-hfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-efuse.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
erweitert werden, wenn auch die Fuse-Dateien gelöscht werden sollen.&lt;br /&gt;
&lt;br /&gt;
Um nun die Fusebits des angeschlossenen Controllers zu programmieren muss lediglichein &amp;quot;make lfuses&amp;quot;, &amp;quot;make hfuses&amp;quot; oder &amp;quot;make efuses&amp;quot; gestartet werden.&lt;br /&gt;
Bei den Fuse-Bits ist besondere Vorsicht geboten, da diese das Programmieren des Controllers unmöglich machen können. Also erst programmieren, wenn man einen HV-Programmierer hat oder ein paar Reserve-AVRs zur Hand ;-)&lt;br /&gt;
&lt;br /&gt;
Um weiterhin den &amp;quot;normalen&amp;quot; Flash beschreiben zu können, ist es wichtig, für das Target &amp;quot;*.hex&amp;quot; im Makefile nicht nur &amp;quot;-R .eeprom&amp;quot; als Parameter zu übergeben sondern zusätzlich noch &amp;quot;-R lfuses -R efuses -R hfuses&amp;quot;. Sonst bekommt AVRDUDE Probleme diese Sections in den Flash (wo sie ja nicht hingehören) zu schreiben.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[AVR_Fuses#Vergleich_der_Fuses_bei_verschiedenen_Programmen|Vergleich der Fuses bei verschiedenen Programmen]]&lt;br /&gt;
&lt;br /&gt;
== Externe Referenzspannung des internen Analog-Digital-Wandlers ==&lt;br /&gt;
&lt;br /&gt;
Die minimale (externe) Referenzspannung des ADC darf nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. z.B. beim ATMEGA8 darf sie laut Datenblatt (S.245, Tabelle 103, Zeile &amp;quot;VREF&amp;quot;) 2,0V nicht unterschreiten. HINWEIS: diese Information findet sich erst in der letzten Revision (Rev. 2486O-10/04) des Datenblatts.&lt;br /&gt;
&lt;br /&gt;
Meiner &amp;lt;!-- Wer? - es gibt inzwischen x Leute die mehr oder weniger viel in diesem Artikel geschrieben haben --&amp;gt; eigenen Erfahrung nach kann man aber (auf eigene Gefahr und natürlich nicht für Seriengeräte) durchaus noch ein klein wenig weiter heruntergehen, bei dem von mir unter die Lupe genommenen ATMEGA8L (also die Low-Voltage-Variante) funktioniert der ADC bei 5V Betriebsspannung mit bis zu VREF=1,15V hinunter korrekt, ab 1,1V und darunter digitalisiert er jedoch nur noch Blödsinn). Ich würde sicherheitshalber nicht unter 1,5V gehen und bei niedrigeren Betriebsspannungen mag sich die Untergrenze für VREF am Pin AREF ggf. nach oben&#039;&#039;&#039;(!)&#039;&#039;&#039; verschieben.&lt;br /&gt;
&lt;br /&gt;
In der letzten Revision des Datenblatts ist außerdem korrigiert, dass ADC4 und ADC5 sehr wohl 10 Bit Genauigkeit bieten (und nicht bloß 8 Bit, wie in älteren Revisionen irrtümlich angegeben.)&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* stdio.h, malloc() &lt;br /&gt;
* Code-Optimierungen (&amp;quot;tricks&amp;quot;), siehe auch Application Note [http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf AVR035: Efficient C Coding for AVR]&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
* SPI siehe [http://www.uni-koblenz.de/~physik/informatik/MCU/SPI.pdf SPI Bus mit Atmel AVR]&lt;br /&gt;
* I²C / TWI Bus [http://www.roboternetz.de/wissen/index.php/TWI]&lt;br /&gt;
* Bootloader (bez. auf boot.h)&lt;br /&gt;
* CAN-Bus&lt;br /&gt;
* Einsatz von einfachen Betriebssystemen auf dem AVR&lt;br /&gt;
* Übersicht zu den C bzw. GCC-predefined Makros (__DATE__, __TIME__,...)&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
* ADC ; &lt;br /&gt;
* Timer&lt;br /&gt;
* USB ; Steuerung mit USB&lt;br /&gt;
* Multiplexen Siebensegment&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28250</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28250"/>
		<updated>2008-05-27T16:27:57Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Erzeugen von Maschinencode */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[AVR-GCC]] erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Progammiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong]. Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern, weder in Assembler noch in einer anderen Sprache. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR Controllern finden. Bei WinAVR gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden stetig weiterentwickelt. Erläuterungen und Beispiele beziehen sich auf den C-Compiler avr-gcc ab Version 3.4 und die avr-libc ab Version 1.4.3. Die Unterschiede zu älteren Versionen werden im Haupttext und Anhang zwar erläutert, Anfängern sei jedoch empfohlen, die aktuellen Versionen zu nutzen (für MS-Windows: aktuelle Version des [[WinAVR]]-Pakets). &lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in PDF-Form erhältlich bei:&lt;br /&gt;
http://www.siwawi.arubi.uni-kl.de/avr_projects/AVR-GCC-Tutorial_-_www_mikrocontroller_net.pdf&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels avr-gcc/avr-libc zu erstellen und zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR Controllers, der vom avr-gcc Compiler unterstützt wird (alle ATmegas und die meisten AT90, siehe Dokumentation der avr-libc für unterstützte Typen). Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man [[AVR#Starterkits|hier]].&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc Compiler und die avr-libc. Kostenlos erhältlich für nahezu alle Plattformen und Betriebssysteme. Für MS-Windows im Paket [[WinAVR]]; für Unix/Linux siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]]).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder nur die eigenen C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
&lt;br /&gt;
* Die [[AVR Checkliste]] durcharbeiten.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/2 GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  auf den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
&lt;br /&gt;
* Die Beispieleprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweisen sind aber auch auf C-Programme übertragbar.&lt;br /&gt;
&lt;br /&gt;
* Einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien, um das Problem nachzuvollziehen, sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z. B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.&amp;amp;nbsp;B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.&amp;amp;nbsp;B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen, um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu zwei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer integrierten Entwicklungsumgebung (IDE = &#039;&#039;&#039;I&#039;&#039;&#039;ntegrated &#039;&#039;&#039;D&#039;&#039;&#039;evelopment &#039;&#039;&#039;E&#039;&#039;&#039;nvironment), bei der alle Einstellungen z.&amp;amp;nbsp;B. in Dialogboxen durchgeführt werden können. Unter Anderem kann AVRStudio ab Version 4.12 (kostenlos auf [http://www.atmel.com/ atmel.com]) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs für den avr-gcc (ohne Anspruch auf Vollständigkeit): AtmanAvr C (relativ günstig), KamAVR (kostenlos), VMLab (ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.&amp;amp;nbsp;B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand der universellen und plattformunabhängigen Vorgehensweise mittels make und Makefiles näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind lediglich Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xff;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5a)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/;  // (5b)&lt;br /&gt;
   }                         // (5c)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (6)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In der mit (1) markierten Zeile, wird eine sogenannte Header-Datei eingebunden. In io.h sind die Registernamen definiert, die im späteren Verlauf genutzt werden.&lt;br /&gt;
&lt;br /&gt;
* Bei (2) beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion main.&lt;br /&gt;
&lt;br /&gt;
* Die Anschlüsse eines AVR (&amp;quot;Beinchen&amp;quot;) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Richtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B zu Ausgängen.&lt;br /&gt;
&lt;br /&gt;
* (4) stellt die Werte der Ausgänge ein. Die den ersten beiden  Bits des Ports zugeordnete Anschluss (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht-aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential).&lt;br /&gt;
&lt;br /&gt;
* (5) ist die so genannte Hauptschleife (main-loop). Dies ist eine Programmschleife, welche kontinuierlich wiederkehrende Befehle enthält. In diesem Beispiel ist sie leer. Der Controller durchläuft die Schleife immer wieder, ohne das etwas passiert (außer das Strom verbraucht wird). Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Wäre die Schleife nicht vorhanden, würde der Zustand des Controllers nach dem Programmende undefiniert.&lt;br /&gt;
&lt;br /&gt;
* (6) wäre das Programmende. Die Zeile ist nur aus Gründen der C- Kompatibilität enthalten: int main(void) besagt, dass die Funktion einen Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein auf den Controller lauffähiges Programm zu übersetzen,  wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen Makefile (ohne Endung) im selben Verzeichnis in dem auch die Datei main.c mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im folgenden Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\tmp\gcc_tut\quickstart&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei main.hex, in der der Code für den AVR enthalten ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.B. über In-System-Programming (ISP) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann (z.B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio), kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine LED leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode an einen dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Exkurs: Makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Plattformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.com) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das Makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den im Makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im Makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten Makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt [[AVR-GCC-Tutorial#EEPROM|Speicherzugriffe]]), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendeten Controllers gesetzt. Eine Liste der von avr-gcc und der avr-libc unterstützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien eintragen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
# nicht auskommentiert: EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Das erstellte Makefile und der Code müssen im gleichen Ordner sein, auch sollte der Dateiname nicht verändert werden. &lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem Makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschinencode erzeugt. Beim Update auf eine neue Compilerversion ist zu beachten, dass diese möglicherweise intern andere Optimierungsalgorithmen verwenden und sich dadurch die Größe des Machinencodes etwas ändert, ohne dass man im Quellcode etwas geändert hat.&lt;br /&gt;
&lt;br /&gt;
Als Orientierungswerte die Größe des Maschinencodes bei verschiedenen Optionen für einen nicht näher spezifizierten relativ kleinen Testcode bei Verwendung einer nicht näher spezifizierten Compilerversion. &lt;br /&gt;
&lt;br /&gt;
* -O0 : 12&#039;217 Byte&lt;br /&gt;
* -O1 : 9&#039;128 Byte&lt;br /&gt;
* -O2 : 1&#039;670 Byte&lt;br /&gt;
* -O3 : 3&#039;004 Byte&lt;br /&gt;
* -Os : 1&#039;695 Byte&lt;br /&gt;
&lt;br /&gt;
Im diesem Testfall führt die Option -O2 mit zum kompaktesten Code, dies  allerdings hier nur mit 25 Bytes &amp;quot;Vorsprung&amp;quot;. Es kann durchaus sein, dass nur wenige Programmerweiterungen dazu führen, dass Compilieren mit -Os wieder in kompakteren Code resultiert.&lt;br /&gt;
&lt;br /&gt;
siehe dazu auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/using_tools.html#gcc_optO avr-libc manual Abschnitt Using the gnu-tools/Compiler-Optionen]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_optflags avr-libc Manual FAQ Nr. 16] (Stand avr-libc Version 1.4.5)&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wird hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (großes S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Taktfrequenz ===&lt;br /&gt;
&lt;br /&gt;
Neuere Versionen der WinAVR/Mfile Vorlage für Makefiles beinhalten die Definition einer Variablen F_CPU (WinAVR 2/2005). Darin wird die Taktfrequenz des Controllers in Hertz eingetragen. Die Definition steht dann im gesamten Projekt ebenfalls unter der Bezeichnung F_CPU zur Verfügung (z.B. um daraus  UART-, SPI- oder ADC-Frequenzeinstellungen abzuleiten).&lt;br /&gt;
&lt;br /&gt;
Die Angabe hat rein &amp;quot;informativen&amp;quot; Charakter, die tatsächliche Taktrate wird über den externen Takt (z.B. Quarz) bzw. die Einstellung des internen R/C-Oszillators  bestimmt. Die Nutzung von F_CPU hat also nur Sinn, wenn die Angabe mit dem tatsächlichen Takt übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Innerhalb neuerer Versionen der avr-libc (ab Version 1.2) wird die Definition der Taktfrequenz (F_CPU) zur Berechnung der Wartefunktionen in delay.h genutzt. Diese funktionieren nur dann korrekt, wenn F_CPU mit der tatsächlichen Taktfrequenz übereinstimmt.&lt;br /&gt;
F_CPU muss dazu jedoch nicht unbedingt im makefile definiert werden. Es reicht aus, wird aber bei mehrfacher Anwendung unübersichtlich, vor &#039;&#039;#include &amp;lt;util/delay.h&amp;gt;&#039;&#039; (veraltet: &#039;&#039;#include &amp;lt;avr/delay.h&amp;gt;&#039;&#039;) ein &#039;&#039;#define F_CPU [hier Takt in Hz]UL&#039;&#039; einzufügen. Bei Nutzung von delay.h ist darauf zu achten, dass die Optimierung des Compilers nicht ausgeschaltet ist, sonst wird sehr viel Code erzeugt und die Wartezeit stimmt nicht mit der gewünschten Zeitspanne überein.&lt;br /&gt;
Vgl. dazu  den [http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html entsprechenden Abschnitt der Dokumentation].&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff: (sollte nur noch in Ausnahmefällen genutzt werden)&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
Die Verwendung von Makefiles bietet noch viele weiter Möglichkeiten, einige davon werden im Anhang [[AVR-GCC-Tutorial#Zus.C3.A4tzliche_Funktionen_im_Makefile|Zusätzliche Funktionen im Makfile]] erläutert.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige (Integer) Datentypen =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei stdint.h definiert. &lt;br /&gt;
Zur Nutzung der standardisierten Typen bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// ab avr-libc Version 1.2.0 möglich und empfohlen:&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// veraltet: #include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef signed char        int8_t;&lt;br /&gt;
typedef unsigned char      uint8_t;&lt;br /&gt;
&lt;br /&gt;
typedef short              int16_t;&lt;br /&gt;
typedef unsigned short     uint16_t;&lt;br /&gt;
&lt;br /&gt;
typedef long               int32_t;&lt;br /&gt;
typedef unsigned long      uint32_t;&lt;br /&gt;
&lt;br /&gt;
typedef long long          int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* int8_t steht für einen 8-Bit Integer mit einem Wertebereich -128 bis +127.&lt;br /&gt;
* uint8_t steht für einen 8-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 255&lt;br /&gt;
&lt;br /&gt;
* int16_t steht für einen 16-Bit Integer mit einem Wertebereich -32768 bis +32767.&lt;br /&gt;
* uint16_t steht für einen 16-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 65535.&lt;br /&gt;
&lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; werden vorzeichenbehaftete Zahlen abgespeichert. Typen mit vorgestelltem &#039;&#039;u&#039;&#039; dienen der Ablage von postiven Zahlen (inkl. 0).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/(Standard) Integer Types&lt;br /&gt;
&lt;br /&gt;
= Bitfelder =&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&lt;br /&gt;
&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in eine einzelne Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitfelder sparen Platz im RAM, zu Lasten von Platz im Flash, verschlechtern aber unter Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.&lt;br /&gt;
&lt;br /&gt;
Wenn man nur eine paar wenige Variablen vom Typ bool verwenden möchte, kann man&lt;br /&gt;
auch die Headerdatei &amp;lt;stdbool.h&amp;gt; einbinden und sich dann wie gewohnt einen Booltyp anlegen. Variablen dieses Typs brauchen dennoch 1 Byte Speicher, ermöglichen aber eine genaue Unterscheidung zwischen Zahlenvariable und boolscher Variable.&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;C-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum können für allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden&lt;br /&gt;
AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039;, wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output).  Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt.&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend&lt;br /&gt;
angepasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O-Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek (avr-libc) stellt auch Funktionen zur Abfrage eines einzelnen Bits eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist, wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind &#039;&#039;nicht erforderlich&#039;&#039;, man kann auch &amp;quot;einfache&amp;quot; C-Syntax verwenden, die universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) wenn das Bit gesetzt und 0 (&amp;quot;falsch&amp;quot;) wenn es nicht gesetzt ist. Die Negierung des Ausdrucks, also  &#039;&#039;&#039;!&#039;&#039;&#039;(Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)), entspricht &#039;&#039;bit_is_clear&#039;&#039; und gibt einen Wert &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) zurück, falls das Bit nicht gesetzt ist,&lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O-Register einfach wie eine Variable setzen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und outp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben von Bits ====&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausdruck:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit (für 1) eines Bytes hat die Bitnummer 0, das &amp;quot;höchstwertige&amp;quot; (für 128) die Nummer 7.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );    /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3);   /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die warten, bis ein bestimmter Zustand auf einem Bit erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend gewartet&amp;quot; wird. D.h., der Programmablauf bleibt an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den Watchdog ein, muss man darauf achten, dass dieser auch noch getriggert wird (zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Änderung: Habe PINx und PORTx vertauscht.PINx ist für Pull up und PORTx für high bzw low. Michael.L  - &lt;br /&gt;
&lt;br /&gt;
Was sollte diese Aenderung?? Den Rest des Abschnitts auch gelesen? Evtl. die hier nicht erlaeuterten Zusatzfunktion der neuen AVRs durcheinandergeworfen? Aenderung verworfen bis mehr Details.  m.thomas. &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
// Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
&lt;br /&gt;
DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
/* mehr Tipparbeit aber übersichtlicher: */&lt;br /&gt;
DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&lt;br /&gt;
/* Einige Compiler erlauben die Eingabe von Konstanten im &lt;br /&gt;
   Binärformat, z.B. gcc in WinAVR, NICHT aber aus&lt;br /&gt;
   GNU-gcc-Quellen selbst erstellte Compiler. Diese Schreib-&lt;br /&gt;
   weise also nicht nutzen, wenn Code mit anderen &lt;br /&gt;
   ausgetauscht werden soll. */&lt;br /&gt;
DDRB = 0b00011111;    /* direkte Zuweisung &lt;br /&gt;
                         - übersichtlicher &lt;br /&gt;
                         - nicht standardkonform &lt;br /&gt;
                         - selten portabel&lt;br /&gt;
                         - nur bei modifiziertem GCC */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
Weitere Beispiele:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
DDRB = 0xff; &lt;br /&gt;
// Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; DDB0 );&lt;br /&gt;
// Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( ( 1 &amp;lt;&amp;lt; DDB3 ) | ( 1&amp;lt;&amp;lt;DDB4) );&lt;br /&gt;
// Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB |= ( 1 &amp;lt;&amp;lt; DDB0) | ( 1 &amp;lt;&amp;lt; DDB3 );&lt;br /&gt;
// Alle Pins auf Eingang:&lt;br /&gt;
DDRB = 0x00;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&#039;&#039;&lt;br /&gt;
* &#039;&#039;zuerst die Bits im PORTx-Register setzen und&#039;&#039;&lt;br /&gt;
* &#039;&#039;anschließend die Datenrichtung auf Ausgang stellen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Daraus ergibt sich die Abfolge für einen Eingangspin:&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze PORTx: interner Pull-Up aktiv&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze DDRx: Ausgang &amp;quot;high&amp;quot;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bei der Reihenfolge erst DDRx und dann PORTx, kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;&#039;nicht&#039;&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, &#039;&#039;&#039;sondern&#039;&#039;&#039; Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.&amp;lt;/font&amp;gt; (Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände.)&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch.  Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVRs haben sogar an den meisten Pins softwaremäßig zuschaltbare interne Pull-Up Widerstände, welche wir natürlich auch verwenden können.&lt;br /&gt;
&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;DDC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== (Tasten-)Entprellung ====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim Schließen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&lt;br /&gt;
&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen als mehrfache Impulse gezählt wird. Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* bei alter avr-libc: #include &amp;lt;avr/delay.h&amp;gt; */      &lt;br /&gt;
&lt;br /&gt;
/* Einfache Funktion zum Entprellen eines Tasters */&lt;br /&gt;
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)&lt;br /&gt;
{&lt;br /&gt;
    if ( ! (*port &amp;amp; (1 &amp;lt;&amp;lt; pin)) )&lt;br /&gt;
    {&lt;br /&gt;
        /* Pin wurde auf Masse gezogen, 100ms warten   */&lt;br /&gt;
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz&lt;br /&gt;
        _delay_ms(50); &lt;br /&gt;
        if ( *port &amp;amp; (1 &amp;lt;&amp;lt; pin) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Anwender Zeit zum Loslassen des Tasters geben */&lt;br /&gt;
            _delay_ms(50);&lt;br /&gt;
            _delay_ms(50); &lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; PB0 );                 /* PIN PB0 auf Eingang (Taster)            */&lt;br /&gt;
    PORTB |= ( 1 &amp;lt;&amp;lt; PB0 );                 /* Pullup-Widerstand aktivieren            */&lt;br /&gt;
    ...&lt;br /&gt;
    if (debounce(&amp;amp;PINB, PB0))             /* Falls Taster an PIN PB0 gedrueckt..    */&lt;br /&gt;
        PORTD = PIND ^ ( 1 &amp;lt;&amp;lt; PD7 );  /* ..LED an Port PD7 an-&lt;br /&gt;
                                   bzw. ausschalten */&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesem Beispiel ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritische Anwendungen sollte man ein anderes Verfahren nutzen (z.B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).&lt;br /&gt;
&lt;br /&gt;
Zum Thema Entprellen siehe auch:&lt;br /&gt;
* Artikel [[Entprellung]]&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls ben&amp;amp;ouml;tigt, kann eine 16-Bit Variable auch recht einfach manuell in ihre zwei 8-Bit Bestandteile zerlegt werden. Folgendes Beispiel demonstriert dies anhand des pseudo- 16-Bit Registers UBRR.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
typedef union {&lt;br /&gt;
        uint16_t i16;&lt;br /&gt;
        struct {&lt;br /&gt;
                uint8_t i8l;&lt;br /&gt;
                uint8_t i8h;&lt;br /&gt;
        };&lt;br /&gt;
} convert16to8;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
convert16to8 baud;&lt;br /&gt;
baud.i16 = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
UBRRH = baud.i8h;&lt;br /&gt;
UBRRL = baud.i8l;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/*alternativ:*/&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint16_t wFoo16;&lt;br /&gt;
uint8_t bFooLow, bFooHigh;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
wFoo16   = 0xAA55;                 /* zu &amp;quot;zerlegende&amp;quot; 16Bit-Integer */&lt;br /&gt;
bFooHigh = (uint8_t)(wFoo16 &amp;gt;&amp;gt; 8); /* MS-Byte */&lt;br /&gt;
bFooLow  = (uint8_t)(wFoo16);      /* LS-Byte */&lt;br /&gt;
...&lt;br /&gt;
#define us0(Data) ((unsigned char *)(&amp;amp;Data))&lt;br /&gt;
#define us1(Data) ((unsigned char *)((&amp;amp;Data)+1))&lt;br /&gt;
bFooHigh = us1(wFoo16);&lt;br /&gt;
bFoolow  = us0(wFoo16);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen (z.B. ATmega8) teilen sich UBRRH und UCSRC die gleiche Memory-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL) welches Register tats&amp;amp;auml;chlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und &amp;amp;uuml;bergibt den Wert &#039;&#039;3&#039;&#039; und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und &amp;amp;uuml;bergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Bei einigen 16-bit Registern (insbesondere die der 16-bit Timer) erfolgen Schreibzugriffe über das sogenannte &#039;&#039;temporary Register&#039;&#039;. Die Reihenfolge der Zugriffe bestimmt, wann der Wert tatsächlich ins Register geschrieben wird. Typisch wird erst das High-Byte beschrieben (xxxH) und intern im temporary Register zwischengespeichert. Nachdem das Low-Byte (xxxL) geschrieben wurde, setzt der Controller mit diesem und dem im temporary Register zwischengespeicherten Wert für das High-Byte das 16-bit Register. Dabei ist zu beachten, dass intern nur ein temporary Register verfügbar ist, welches in Interruptroutinen mglw. mit einem anderen Wert überschrieben wird, wenn dort ebenfalls 16-bit Register beschrieben werden. avr-gcc/avr-libc berücksichtigen die korrekte Reihenfolge automatisch, wenn die Register mit ihrem &amp;quot;16-bit Label&amp;quot; (ohne H bzw. L) angesprochen werden, dabei ist der Schutz des temporary Registers vor Überschreiben durch Interruptroutinen dennoch zu beachten (im Zweifel beim Schreibzugriff die Interrupts kurzzeitig global deaktivieren).&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t key_pressed(const volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if ( last_state == ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) ) ) {&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    i = key_pressed( &amp;amp;PINB, PB1 );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Im Übrigen können mit volatile gekennzeichnete Variablen auch als const deklariert werden, um sicherzustellen, dass sie nur noch von der Hardware änderbar sind.&lt;br /&gt;
&lt;br /&gt;
= Der UART =&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Über den [[UART]] kann ein AVR leicht mit einer [[RS-232]]-Schnittstelle eines PC oder sonstiger Geräte mit &amp;quot;serieller Schnittstelle&amp;quot; verbunden werden. &lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungen des UART:&lt;br /&gt;
&lt;br /&gt;
* Debug-Schnittstelle: z.B. zur Anzeige von Zwischenergebnissen (&amp;quot;printf-debugging&amp;quot; - hier besser &amp;quot;UART-debugging&amp;quot;) auf einem PC. Auf dem Rechner reicht dazu ein [[RS-232#Terminalprogramme|Terminalprogramm]] (MS-Windows: Hyperterm oder besser Bray-Terminal, [http://www.mikrocontroller.net/forum/read-8-155472.html#new HTerm]; Unix/Linux z.B. minicom). Ein direkter Anschluss ist aufgrund unterschiedlicher Pegel nicht möglich, jedoch sind entsprechende Schnittstellen-ICs wie z.B. ein MAX232 günstig und leicht zu integrieren. Rechner ohne serielle Schnittstelle können über fertige USB-seriell-Adapter angeschlossen werden. &lt;br /&gt;
* &amp;quot;Mensch-Maschine Schnittstelle&amp;quot;: z.B. Konfiguration und Statusabfrage über eine &amp;quot;Kommandozeile&amp;quot; oder Menüs (siehe z.B. Forumsbeitrag [http://www.mikrocontroller.net/topic/52985 Auswertung RS232-Befehle]) &lt;br /&gt;
* Übertragen von gespeicherten Werten: z.B. bei einem Datenlogger&lt;br /&gt;
* Anschluss von Geräten mit serieller Schnittstelle (z.B. (Funk-)Modems, Mobiltelefone, Drucker, Sensoren, &amp;quot;intelligente&amp;quot; LC-Displays, GPS-Empfänger). &lt;br /&gt;
* &amp;quot;Feldbusse&amp;quot; auf RS485/RS422-Basis mittels entsprechenden Bustreiberbausteinen (z.B. MAX485)&lt;br /&gt;
* DMX, Midi etc.&lt;br /&gt;
* LIN-Bus (&#039;&#039;&#039;L&#039;&#039;&#039;ocal &#039;&#039;&#039;I&#039;&#039;&#039;nterconnect &#039;&#039;&#039;N&#039;&#039;&#039;etwork): Preiswerte Sensoren/Aktoren in der Automobiltechnik und darüber hinaus&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben ein bis zwei vollduplexfähigen UART (&#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) schon eingebaut (&amp;quot;Hardware-UART&amp;quot;). &lt;br /&gt;
Übrigens: Vollduplex heißt nichts anderes, als dass der Baustein gleichzeitig senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega, ATtiny) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterscheidet sich vom UART hauptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurationsregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit zeigt an, ob der Sendepuffer bereit ist, um ein zu sendendes Zeichen aufzunehmen. Das Bit wird vom AVR gesetzt (1), wenn der Sendepuffer leer ist. Es wird gelöscht (0), wenn ein Zeichen im Sendedatenregister vorhanden ist und noch nicht in das Sendeschieberegister übernommen wurde. Atmel empfiehlt aus Kompatibilitätsgründen mit kommenden µC, UDRE auf 0 zu setzen, wenn das UCSRA Register beschrieben wird.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
\mathrm{UBRR} = \frac{\mathrm{Taktfrequenz}}{\mathrm{Baudrate} \cdot 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS-232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. &lt;br /&gt;
&amp;lt;!-- &amp;quot;Hackerloesung&amp;quot; auskommentiert - nicht so gut in einem &amp;quot;Einsteiger-Tutorial&amp;quot; - mthomas&lt;br /&gt;
Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfiehlt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== UART initialisieren ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben. Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach gewünschter Funktionsweise die benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Da wir vorerst nur senden möchten und noch keine Interrupts auswerten wollen, gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs mit USART haben mehrere Konfigurationsregister und erfordern eine etwas andere Konfiguration. Für einen ATmega16 z.B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                 // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);     // Asynchron 8N1&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist noch das Baudratenregister &#039;&#039;&#039;UBRR&#039;&#039;&#039; einzustellen. Bei neueren AVRs besteht es aus zwei Registern &#039;&#039;&#039;UBRRL&#039;&#039;&#039; und &#039;&#039;&#039;UBRRH&#039;&#039;&#039;. Der Wert dafür ergibt sich aus der angegebenen Formel durch Einsetzen der Taktfrequenz und der gewünschten Übertragungsrate. Das Berechnen der Formel wird dem [[C-Präprozessor|Präprozessor]] überlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann&lt;br /&gt;
   F_CPU im Makefile definiert werden, eine nochmalige Definition&lt;br /&gt;
   hier wuerde zu einer Compilerwarnung fuehren. Daher &amp;quot;Schutz&amp;quot; durch&lt;br /&gt;
   #ifndef/#endif &lt;br /&gt;
&lt;br /&gt;
   Dieser &amp;quot;Schutz&amp;quot; kann zu Debugsessions führen, wenn AVRStudio &lt;br /&gt;
   verwendet wird und dort eine andere, nicht zur Hardware passende &lt;br /&gt;
   Taktrate eingestellt ist: Dann wird die folgende Definition &lt;br /&gt;
   nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) &lt;br /&gt;
   von AVRStudio - daher Ausgabe einer Warnung falls F_CPU&lt;br /&gt;
   noch nicht definiert: */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000&amp;quot;&lt;br /&gt;
#define F_CPU 4000000UL    // Systemtakt in Hz &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600UL          // Baudrate&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) // Fehler in Promille, 1000 = kein Fehler.&lt;br /&gt;
&lt;br /&gt;
#if ((BAUD_ERROR&amp;lt;990) || (BAUD_ERROR&amp;gt;1010))&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;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    UBRR = UBRR_VAL;&lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&amp;lt;!-- mthomas: warum nicht UL?, wird von AVRStudio auch mit UL übergeben --&amp;gt;&lt;br /&gt;
Wieder für den Mega16 mit zwei Registern für die Baudrateneinstellung eine etwas andere Programmierung. Wichtig ist, dass UBRRH &#039;&#039;&#039;vor&#039;&#039;&#039; UBRRL geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmega16 */&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einige AVR (z.B. ATmega169, ATmega48/88/168, AT90CAN jedoch nicht für z.B. ATmega16/32, ATmega128, ATtiny2313) wird durch die Registerdefinitionen der avr-libc (io*.h) auch für Controller mit zwei UBRR-Registern (UBRRL/UBRRH) ein UBRR bzw. UBRR0 als &amp;quot;16-bit-Register&amp;quot; definiert und man kann auch Werte direkt per UBRR = UBRR_VAL zuweisen. Intern werden dann zwei Zuweisungen für UBRRH und UBRRL generiert. Dies ist nicht bei allen Controllern möglich, da die beiden Register nicht bei allen aufeinanderfolgende Addressen aufweisen. Die getrennte Zuweisung an UBRRH und UBRRL wie im Beispiel gezeigt ist universeller und portabler und daher vorzuziehen.&lt;br /&gt;
&lt;br /&gt;
Die Makros sind sehr praktisch, da sie sowohl automatisch den Wert für UBRR als auch den Fehler in der generierten Baudrate berechnen und im Falle einer Überschreitung (+/-1%) einen Fehler und somit Abbruch im Compilerablauf generieren. Damit können viele Probleme mit &amp;quot;UART sendet komische Zeichen&amp;quot; vermieden werden. Ausserdem kann man mühelos die Einstellung an eine neue Taktfrequenz bzw. Baudrate anpassen, ohne selber rechnen oder in Tabellen nachschlagen zu müssen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. Vorher ist zu prüfen, ob das UART-Modul bereit ist, das zu sendende Zeichen entgegenzunehmen. Die Bezeichnungen des/der Statusregisters mit dem Bit UDRE ist abhängig vom Controllertypen (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    // bei AVR mit einem UART (&amp;quot;classic AVR&amp;quot; z.B. AT90S8515)&lt;br /&gt;
    while (!(USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                  /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&lt;br /&gt;
    /** ODER **/&lt;br /&gt;
&lt;br /&gt;
    // bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                    /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe &amp;quot;String senden&amp;quot; wird durch zwei Funktionen abgearbeitet. Die universelle/controllerunabhängige Funktion uart_puts übergibt jeweils ein Zeichen der Zeichenkette an eine Funktion uart_putc, die abhängig von der vorhandenen Hardware implementiert werden muss. In der Funktion zum Senden eines Zeichens ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// putc fuer AVR mit einem UART (z.B. AT90S8515)&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while(!(USR &amp;amp; (1 &amp;lt;&amp;lt; UDRE)))  /* warte, bis UDR bereit */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = c;                     /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** ODER **/&lt;br /&gt;
&lt;br /&gt;
// bei neueren AVRs andere Bezeichnung fuer die Statusregister, hier ATmega16:&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich */&lt;br /&gt;
    {&lt;br /&gt;
    }                             &lt;br /&gt;
&lt;br /&gt;
    UDR = c;                      /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* puts ist unabhaengig vom Controllertyp */&lt;br /&gt;
void uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != &#039;\0&#039; also ungleich dem &amp;quot;String-Endezeichen&amp;quot; */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die in uart_putc verwendeten Schleifen, in denen gewartet wird bis die UART-Hardware zum senden bereit ist, sind insofern etwas kritisch, da während des Sendens eines Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. empfangenen Zeichen/Bytes zwischengespeichert und in Interruptroutinen an die U(S)ART-Hardware weitergegeben bzw. von ihr ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&lt;br /&gt;
&lt;br /&gt;
=== Schreiben von Variableninhalten ===&lt;br /&gt;
&lt;br /&gt;
Sollen Inhalte von Variablen (Ganzzahlen, Fließkomma) in &amp;quot;menschenlesbarer&amp;quot; Form gesendet werden, ist vor dem Transfer eine Umwandlung in Zeichen (&amp;quot;ASCII&amp;quot;) erforderlich. Bei nur einer Ziffer ist diese Umwandlung relativ einfach: man addiert den ASCII-Wert von Null zur Ziffer und kann diesen Wert direkt senden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_putc (s.o.)&lt;br /&gt;
&lt;br /&gt;
// Ausgabe von 0123456789&lt;br /&gt;
char c;&lt;br /&gt;
&lt;br /&gt;
for (uint8_t i=0; i&amp;lt;=9; ++i) {&lt;br /&gt;
    c = i + &#039;0&#039;;&lt;br /&gt;
    uart_putc( c );&lt;br /&gt;
    // verkuerzt: uart_putc( i + &#039;0&#039; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll mehr als eine Ziffer ausgegeben werden, bedient man sich zweckmäßigerweise vorhandener Funktionen zur Umwandlung von Zahlen in Zeichenketten/Strings. Die Funktion der avr-libc zur Umwandlung von vorzeichenbehafteten 16bit-Ganzzahlen (int16_t) in Zeichenketten heißt &#039;&#039;itoa&#039;&#039; (Integer to ASCII). Man muss der Funktion einen Speicherbereich zur Verarbeitung (buffer) mit Platz für alle Ziffern, das String-Endezeichen (&#039;\0&#039;) und evtl. das Vorzeichen bereitstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   char s[7];&lt;br /&gt;
   int16_t i = -12345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   itoa( i, s, 10 ); // 10 fuer radix -&amp;gt; Dezimalsystem&lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // da itoa einen Zeiger auf den Beginn von s zurueckgibt verkuerzt auch:&lt;br /&gt;
   uart_puts( itoa( i, s, 10 ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für vorzeichenlose 16bit-Ganzzahlen (uint16_t) exisitert &#039;&#039;utoa&#039;&#039;. Die Funktionen für 32bit-Ganzzahlen (int32_t und uint32_t) heißen &#039;&#039;ltoa&#039;&#039; bzw. &#039;&#039;ultoa&#039;&#039;. Da 32bit-Ganzzahlen mehr Stellen aufweisen können, ist ein entsprechend größerer Pufferspeicher vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Auch Fließkommazahlen (float/double) können mit breits vorhandenen Funktionen in Zeichenfolgen umgewandelt werden, dazu existieren die Funktionen &#039;&#039;dtostre&#039;&#039; und &#039;&#039;dtostrf&#039;&#039;. dtostre nutzt Exponentialschreibweise (&amp;quot;engineering&amp;quot;-Format). (Hinweis: z.Zt. existiert im avr-gcc kein &amp;quot;echtes&amp;quot; double, intern wird immer mit &amp;quot;einfacher Genauigkeit&amp;quot;, entsprechend float, gerechnet.) &lt;br /&gt;
&lt;br /&gt;
dtostrf und dtostre benötigen die libm.a der avr-libc. Bei Nutzung von Makefiles ist der Parameter -lm in in LDFLAGS anzugeben (Standard in den WinAVR/mfile-Makefilevorlagen). Nutzt man AVRStudio als IDE für den GNU-Compiler (gcc-Plugin) ist die libm.a unter Libaries auszuwählen: Project -&amp;gt; Configurations Options -&amp;gt; Libaries -&amp;gt; libm.a mit dem Pfeil nach rechts einbinden. Siehe auch die [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|FAQ]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
/* lt. avr-libc Dokumentation:&lt;br /&gt;
char* dtostrf(&lt;br /&gt;
  double __val,&lt;br /&gt;
  char   __width,&lt;br /&gt;
  char   __prec,&lt;br /&gt;
  char * __s&lt;br /&gt;
)  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   // Pufferspeicher ausreichend groß&lt;br /&gt;
   // evtl. Vorzeichen + width + Endezeichen:&lt;br /&gt;
   char s[8]; &lt;br /&gt;
   float f = -12.345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   dtostrf( f, 6, 3, s ); &lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // verkürzt: uart_puts( dtostrf( f, 7, 3, s ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfangen ==&lt;br /&gt;
=== einzelne Zeichen empfangen ===&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von Zeichen muss der Empfangsteil des UART bei der Initialisierung aktiviert werden, indem das RXEN-Bit im jeweiligen Konfigurationsregister (UCSRB bzw UCSR0B/UCSR1B) gesetzt wird. Im einfachsten Fall wird solange gewartet, bis ein Zeichen empfangen wurde, dieses steht dann im UART-Datenregister (UDR bzw. UDR0 und UDR1 bei AVRs mit 2 UARTS) zur Verfügung (sogen. &amp;quot;Polling-Betrieb&amp;quot;). Ein Beispiel für den ATmega16:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Zusaetzlich zur Baudrateneinstellung und der weiteren Initialisierung: */&lt;br /&gt;
void Usart_EnableRX()&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= ( 1 &amp;lt;&amp;lt; RXEN );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion blockiert den Programmablauf. Alternativ kann das RXC-Bit in einer Programmschleife abgefragt werden und dann nur bei gesetztem RXC-Bit UDR ausgelesen werden. Eleganter und in den meisten Anwendungsfällen &amp;quot;stabiler&amp;quot; ist die Vorgehensweise, die empfangenen Zeichen in einer Interrupt-Routine einzulesen und zur späteren Verarbeitung in einem Eingangsbuffer (FIFO-Buffer) zwischenzuspeichern. Dazu existieren fertige und gut getestete [[Libraries|Bibliotheken]] &amp;lt;!-- &amp;quot;echte Libraries&amp;quot; (.a) wie im Verweis beschrieben sind hier eigentlich nicht gemeint, verwirrt hier etwas, da AVR-&amp;quot;Libraries&amp;quot; meist per #defines anpassbare Source-Codes sind, vielleicht so: --&amp;gt; und Quellcodekomponenten (z.B. UART-Library von P. Fleury, procyon-avrlib und einige in der &amp;quot;Academy&amp;quot; von avrfreaks.net).&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html Dokumenation der avr-libc/stdlib.h]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_printf Die Nutzung von printf]&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&amp;lt;!-- nimmermehr: * siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (printf etc.) im [[AVR-Tutorial:_UART]]. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: 9bit&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Zeichenketten (Strings) ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Zeichenketten, muß man sich zunächst darüber im klaren sein, daß es ein Kriterium geben muß, an dem der µC erkennen kann, wann ein String zu Ende ist. Sehr oft wird dazu das Zeichen &#039;Return&#039; benutzt, um das Ende eines Strings zu markieren. Dies ist vom Benutzer einfach eingebbar und er ist auch daran gewöhnt, daß er eine Eingabezeile mit einem Druck auf die Return Taste abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Prinzipiell gibt es jedoch keine Einschränkung bezüglich dieses speziellen Zeichens. Es muß nur sichergestellt werden, daß dieses spezielle &#039;Ende eines Strings&#039; - Zeichen nicht mit einem im Text vorkommenden Zeichen verwechselt werden kann. Wenn also im zu übertragenden Text beispielsweise kein &#039;;&#039; vorkommt, dann spricht nichts dagegen, einen String mit einem &#039;;&#039; abschließen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird die durchaus übliche Annahme getroffen, daß eine Stringübertragung identisch ist mit der Übertragung einer Textzeile und daher mit einem Return (&#039;\n&#039;) abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Das Problem der Übertragung eines Strings reduziert sich damit auf die Aufgabenstellung: Empfange und Sammle Zeichen in einem char Array, bis entweder das Array voll ist oder das &#039;String Ende Zeichen&#039; empfangen wurde. Danach wird der empfangene Text noch mit einem &#039;\0&#039; Zeichen abgeschlossen um einen Standard C-String daraus zu machen, mit dem dann weiter gearbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_gets( char* Buffer, uint8_t MaxLen )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t NextChar;&lt;br /&gt;
  uint8_t StringLen = 0;&lt;br /&gt;
&lt;br /&gt;
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen&lt;br /&gt;
&lt;br /&gt;
                                  // Sammle solange Zeichen, bis:&lt;br /&gt;
                                  // * entweder das String Ende Zeichen kam&lt;br /&gt;
                                  // * oder das aufnehmende Array voll ist&lt;br /&gt;
  while( NextChar != &#039;\n&#039; &amp;amp;&amp;amp; StringLen &amp;lt; MaxLen - 1 ) {&lt;br /&gt;
    *Buffer++ = NextChar;&lt;br /&gt;
    StringLen++;&lt;br /&gt;
    NextChar = uart_getc();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
                                  // Noch ein &#039;\0&#039; anhängen um einen Standard&lt;br /&gt;
                                  // C-String daraus zu machen&lt;br /&gt;
  *Buffer = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf ist darauf zu achten, dass das empfangende Array auch mit einer&lt;br /&gt;
vernünftigen Größe definiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
  char Line[40];      // String mit maximal 39 zeichen&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) );&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Interruptbetrieb==&lt;br /&gt;
&lt;br /&gt;
Beim ATMEGA8 ist es möglich, dass RXCIE Bit im Register UCSRB zu setzen.&lt;br /&gt;
Dieser Interrupt wird immer ausglöst, wenn Daten erfolgreich empfangen wurden.&lt;br /&gt;
Zusätzlich braucht man die Routine:&lt;br /&gt;
&lt;br /&gt;
ISR (USART_RXC_vect) {&lt;br /&gt;
   //irgendein Code&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
natürlich muss &amp;quot;Global Interrupt Enable&amp;quot; Aktiviert sein.&lt;br /&gt;
!! Nur getestet beim ATMEGA8 !!&lt;br /&gt;
&lt;br /&gt;
[BAUSTELLE! Aus Lerngründen eventuell als eigenen UART-Interrupt-Block hinter den grundlegenden Interrupt-Teil im Tutorial und hier eine kurze Einführung und einen Verweis darauf anbieten.]&lt;br /&gt;
&lt;br /&gt;
* Unterschied Polling-Betrieb (bisher, oben) und Interrupt-Betrieb&lt;br /&gt;
* Empfangen (Receive)&lt;br /&gt;
** Verändertes UART-Init, ISR (RXC), ggf. Fallstricke ([http://www.mikrocontroller.net/topic/84256#707214 UDR in der ISR lesen!]), Philosophie einer ISR (kurz und schmerzlos), Datenaustausch ISR zu Restprogramm (volatile)&lt;br /&gt;
** Einfachstbeispiel ([http://www.mikrocontroller.net/topic/84228#707052 Echo] (noch buggy beim Datenzugriff, siehe Lit. 2+3!)), ggf. LED zur ISR-Empfangsanzeige oder Overflow-Anzeige&lt;br /&gt;
* FIFO-Puffer, Ringpuffer&lt;br /&gt;
* Senden (Transmit)&lt;br /&gt;
** Variante &amp;quot;UART Data Register Empty&amp;quot; (UDRE) &lt;br /&gt;
** Variante &amp;quot;UART Transmit Complete&amp;quot; (TXC) &lt;br /&gt;
* Fertige UART-Bibliotheken (-&amp;gt;Fleury, -&amp;gt;Procyon)&lt;br /&gt;
* Literatur: &lt;br /&gt;
** 1/ [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=48188 avrfreaks.net Tutorial] inkl. Diskussion (engl.)&lt;br /&gt;
** 2/ avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_16bitio Why do some 16-bit timer registers sometimes get trashed?]&lt;br /&gt;
** 3/ [[Interrupt]] und atomarer Datenzugriff&lt;br /&gt;
&lt;br /&gt;
==Software-UART==&lt;br /&gt;
&lt;br /&gt;
Falls die Zahl der vorhandenen Hardware-UARTs nicht ausreicht, können weitere Schnittstellen über sogennante Software-UARTs ergänzt werden. Es gibt dazu (mindestens) zwei Ansätze: &lt;br /&gt;
* Der bei AVRs üblichste Ansatz basiert auf dem Prinzip, dass ein externer Interrupt-Pin für den Empfang (&amp;quot;RX&amp;quot;) genutzt wird. Das Startbit löst den Interrupt aus, in der Interrupt-Routine (ISR) wird der externe Interrupt deaktiviert und ein Timer aktiviert. In der Interrupt-Routine des Timers wird der Zustand des Empfangs-Pins entsprechend der Baudrate abgetastet. Nach Empfang des Stop-Bits wird der externe Interrupt wieder aktiviert. Senden kann über einen beliebigen Pin (&amp;quot;TX&amp;quot;) erfolgen, der entsprechend der Baudrate und dem zu sendenden Zeichen auf 0 oder 1 gesetzt wird. Die Implementierung ist nicht ganz einfach, es existieren dazu aber fertige Bibliotheken (z.B. bei [http://www.avrfreaks.net/ avrfreaks] oder in der [http://hubbard.engr.scu.edu/embedded/avr/avrlib/ Procyon avrlib]).&lt;br /&gt;
* Ein weiterer Ansatz erfordert keinen Pin mit &amp;quot;Interrupt-Funktion&amp;quot; aber benötigt mehr Rechenzeit. Jeder Input-Pin kann als Empfangspin (RX) dienen. Über einen Timer wird der Zustand des RX-Pins mit einem vielfachen der Baudrate abgetastet (dreifach scheint üblich) und High- bzw. Lowbits anhand einer Mindestanzahl identifiziert. (Beispiel: &amp;quot;Generic Software Uart&amp;quot; Application-Note von IAR)&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (z.B. ATtiny26 oder ATmega48,88,168,169) verfügen über ein Universal Serial Interface (USI), das teilweise UART-Funktion übernehmen kann. Atmel stellt eine Application-Note bereit, in der die Nutzung des USI als UART erläutert wird (im Prinzip &amp;quot;Hardware-unterstützter Software-UART&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==Handshaking==&lt;br /&gt;
Wenn der Sender ständig sendet, wird irgendwann der Fall eintreten, daß der Empfänger nicht bereit ist, neue Zeichen zu empfangen. In diesem Fall muß durch ein &#039;&#039;&#039;Handshake-Verfahren&#039;&#039;&#039; die Situation bereinigt werden. Handshake bedeutet nichts anderes, als daß der Empfänger dem Sender mitteilt, daß er zur Zeit keine Daten annehmen kann und der Sender die Übertragung der nächsten Zeichen solange einstellen soll, bis der Empfänger signalisiert, daß er wieder Zeichen aufnehmen kann.&lt;br /&gt;
===Hardwarehandshake (RTS/CTS)===&lt;br /&gt;
Beim Hardwarehandshake werden zusätzlich zu den beiden Daten-Übertragungsleitungen noch 2 weitere Leitungen benötigt: &#039;&#039;&#039;RTS&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;equest &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end) und &#039;&#039;&#039;CTS&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end). Jeder der beiden Kommunikationspartner ist verpflichtet, bevor ein Zeichen gesendet wird, den Zustand der &#039;&#039;&#039;RTS&#039;&#039;&#039; Leitung zu überprüfen. Nur wenn die Gegenstelle darauf Empfangsbereitschaft signalisiert, darf das Zeichen gesendet werden. Um der Gegenstelle zu signalisieren, daß sie zur Zeit keine Zeichen schicken soll, wird die Leitung &#039;&#039;&#039;CTS&#039;&#039;&#039; benutzt.&lt;br /&gt;
&lt;br /&gt;
===Softwarehandshake (XON/XOFF)===&lt;br /&gt;
Beim Softwarehandshake sind keine speziellen Leitungen notwendig. Statt dessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, daß Senden einzustellen bzw. wieder aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;XOFF&#039;&#039;&#039; Aufforderung das Senden einzustellen&lt;br /&gt;
* &#039;&#039;&#039;XON&#039;&#039;&#039;  Gegenstelle darf wieder senden&lt;br /&gt;
&lt;br /&gt;
Nachteilig bei einem Softwarehandshake ist es, daß dadurch keine direkte binäre Datenübertragung mehr möglich ist. Von den möglichen 256 Bytewerten werden ja 2 (nämlich &#039;&#039;&#039;XON&#039;&#039;&#039; und &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus.&lt;br /&gt;
&lt;br /&gt;
==Fehlersuche==&lt;br /&gt;
Erstaunlich oft wird im Forum der Hilferuf laut: &amp;quot;Meine UART funktioniert nicht, was mache ich falsch&amp;quot;. In der überwiegenden Mehrzahl der Fälle stellt sich dann heraus, daß es sich um ein Hardwareproblem handelt, wobei da wiederrum der Löwenanteil auf das Konto einer nicht korrekt eingestellten Taktrate geht: Der µC benutzt nicht einen angeschlossenen Quarz, so wie er auch im Programm eingetragen ist, sondern läuft immer noch mit dem internen RC-Takt. Daraus resultiert aber auch, daß der Baudraten Konfigurationswert falsch berechnet wird.&lt;br /&gt;
&lt;br /&gt;
Eine Checkliste zum Aufspüren solcher Fehler findet sich [[AVR_Checkliste#UART.2FUSART|hier]].&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Analoge Eingangswerte werden in der Regel über den AVR Analog-Digital-Converter (AD-Wandler, ADC) eingelesen, der in vielen Typen verfügbar ist (typisch 10bit Auflösung). Durch diesen werden analogen Signale (Spannungen) in digitale Zahlenwerte gewandelt. Bei AVRs, die über keinen internen AD-Wandler verfügen (z.B. ATmega162, ATtiny2313), kann durch externe Beschaltung (R/C-Netzwerk und &amp;quot;Zeitmessung&amp;quot;) die Funktion des AD-Wandlers &amp;quot;emuliert&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
Es existieren keine AVRs mit eingebautem Digital-Analog-Konverter (DAC). Diese Funktion muss durch externe Komponenten nachgebildet werden (z.B. PWM und &amp;quot;Glättung&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Unabhängig davon besteht natürlich immer die Möglichkeit, spezielle Bausteine zur Analog-Digital- bzw. Digital-Analog-Wandlung zu nutzen und diese über eine digitale Schnittstelle (z.b. SPI oder I2C) mit einem AVR anzusteuern.&lt;br /&gt;
&lt;br /&gt;
== AC (Analog Comparator) ==&lt;br /&gt;
&lt;br /&gt;
Der Comparator vergleicht 2 Spannungen an den Pins AIN0 und AIN1 und gibt einen Status aus welche der beiden Spannungen größer ist. AIN0 Dient dabei als Referenzspannung (Sollwert) und AIN1 als Vergleichsspannung (Istwert). Als Referenzspannung kann auch alternativ eine interne Referenzspannung ausgewählt werden.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Liegt die Vergleichsspannung (IST) unter der der Referenzspannung (SOLL) gibt der Comperator eine logische 0 aus. ISt die Vergleichsspannung hingegen größer als die Referenzspannung wird eine logische 1 ausgegeben.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Der Comperator arbeitet völlig autark bzw. parallel zum Prozessor. Für mobile Anwendungen empfiehlt es sich ihn abzuschalten sofern er nicht benötigt wird, da er ansonsten Strom benötigt. Der Comparator kann Interruptgesteuert abgefragt werden oder im Pollingbetrieb.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Steuer- bzw. Statusregister ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ACSR (0x28) - Analog Comparator Status Register&amp;lt;BR&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACD&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;AINBG&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACO&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACI&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | n/a&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 7 ACD - Analog Comperator Disable: 0 = Comparator ein, 1 = Comparator aus. Wird dieses Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 ACIE ggf. abgeschaltet werden.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 6 AINBG - Analog Comparator Bandgap Select: Ermöglicht das umschalten zwischen interner und externer Referenzspannung. 1 = interne (~1,3 Volt), 0 = externe Referenzspannung (an Pin AIN0)&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 5 ACO - Analog Coparator Output: Hier wird das Ergebnis des Vergleichs angezeigt. Es liegt typischerweise nach 1-2 Taktzyklen vor. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ergebnis: &amp;lt;BR&amp;gt;IST &amp;lt; SOLL --&amp;gt; 0&amp;lt;BR&amp;gt;&lt;br /&gt;
IST &amp;gt; SOLL --&amp;gt; 1&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4 ACI - Analog Comparator Interrupt Flag: Dieses Bit wird von der Hardware gesetzt wenn ein Interruptereignis das in Bit 0 und 1 definiert ist eintritt. Dieses Bit löst noch keinen Interrupt aus! Die Interruptroutine wird nur dann ausgeführt wenn das Bit 3 ACIE gesetzt ist und global Interrupts erlaubt sind (I-Bit in SREG=1). Das Bit 4 ACI wird wieder gelöscht wenn die Interruptroutine ausgeführt wurde oder wenn manuell das Bit auf 1 gesetzt wird. Das Bit kann für Abfragen genutzt werden, steuert oder konfuguriert aber nicht den Comparator.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 3 ACIE - Analog Comparator Interrupt Enable: Ist das Bit auf 1 gesetzt wird immer ein Interrupt ausgelöst wenn das Ereignis das in Bit 1 und 0 definiert ist eintritt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 2 ACIC - Analog Comparator Input Capture Enable: Wird das Bit gesetzt wird der Comparatorausgang intern mit dem Counter 1 verbunden. Es könnten damit z.b. die Anzahl der Vergleiche im Counter1 gezählt werden. Um den Comparator an den Timer1 Input Capture Interrupt zu verbinden muß im Timerregister das TICIE1 Bit auf 1 gesetzt werden. Der Trigger wird immer dann ausgelöst wenn das in Bit 1 und 0 definierte Ereignis eintritt.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 1,0 ACIS1,ACIS0 - Analog Comparator Interrupt select: Hier wird definiert welche Ereignisse einen Interrupt auslösen sollen:&amp;lt;BR&amp;gt;&lt;br /&gt;
00 = Interrupt auslösen bei jedem Flankenwechsel&amp;lt;BR&amp;gt;&lt;br /&gt;
10 = Interrupt auslösen bei fallender Flanke&amp;lt;BR&amp;gt;&lt;br /&gt;
11 = Interrupt auslösen bei steigender Flanke&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Werden diese Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 gelöscht werden.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der Analog-Digital-Konverter (ADC) wandelt analoge Signale in digitale Werte um, welche vom Controller interpretiert werden können. Einige AVR-Typen haben bereits einen mehrkanaligen Analog-Digital-Konverter eingebaut. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst werden kann, wird durch die Auflösung des ADC in Anzahl Bits angegeben, man hört bzw. liest jeweils von 8-Bit-ADC oder 10-Bit-ADC oder noch höher. ADCs die in AVRs enthalten sind haben zur Zeit eine maximale Auflösung von 10-Bit.&lt;br /&gt;
&lt;br /&gt;
Ein ADC mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des Analog-Digital-Konverters ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Der interne ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem Analog-Digital-Wandler (ADC) zurückgreifen, die über mehrere Kanäle verfügen. Kanäle heißt in diesem Zusammenhang, dass zwar bis zu zehn analoge Eingänge am AVR verfügbar sind, aber nur ein &amp;quot;echter&amp;quot; Analog-Digital-Wandler zur Verfügung steht, vor der eigentlichen Messung ist also einzustellen, welcher Kanal (&amp;quot;Pin&amp;quot;) mit dem Wandler verbunden und gemessen wird.&lt;br /&gt;
&lt;br /&gt;
Die Umwandlung innerhalb des AVR basiert auf der schrittweisen Näherung. Beim AVR müssen die Pins &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AVCC&#039;&#039;&#039; beschaltet werden. Für genaue Messungen sollte AVCC über ein L-C Netzwerk mit VCC verbunden werden, um Spannungsspitzen und -einbrüche vom Analog-Digital-Wandler fernzuhalten. Im Datenblatt findet sich dazu eine Schaltung, die 10uH und 100nF vorsieht.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Analog-Digital-Wandlung wird auf eine Referenzspannung bezogen. Aktuelle AVRs bieten drei Möglichkeiten zur Wahl dieser Spannung:&lt;br /&gt;
&lt;br /&gt;
* Eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; am Anschlusspin &#039;&#039;&#039;AREF&#039;&#039;&#039;. Die minimale (externe) Referenzspannung darf jedoch nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. &lt;br /&gt;
&lt;br /&gt;
* Verfügt der AVR über eine interne Referenzspannung, kann diese genutzt werden. Alle aktuellen AVRs mit internem AD-Wandler sollten damit ausgestattet sein (vgl. Datenblatt: 2,56V oder 1,1V je nach Typ). Das Datenblatt gibt auch über die Genauigkeit dieser Spannung Auskunft.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;interne&#039;&#039;&#039; Referenzspannung wird auf &#039;&#039;&#039;Vcc&#039;&#039;&#039; bezogen, eine externe Referenzspannung auf &#039;&#039;&#039;GND (Masse)&#039;&#039;&#039;. Davon unabhängig werden digitalisierte Spannungen &#039;&#039;&#039;immer&#039;&#039;&#039; auf GND bezogen. Beim ATMEGA8 z.B. ist der Pin AREF über 32kOhm mit GND verbunden, d.h. man muss diese doch extrem niedrige Eingangsimpedanz mit in die Berechnung für einen Spannungsteiler einbeziehen, bzw. kann diesen Widerstand als R2 gleich mit benutzen. Formel für Spannungsteiler: Udiv = U / ((R1 + R2) / R2)&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung von Vcc oder der internen Referenz wird empfohlen, einen Kondensator zwischen dem AREF-Pin und GND anzuordnen. Die Festlegung, welche Spannungsreferenz genutzt wird, erfolgt z.B. beim ATmega16 mit den Bits REFS1/REFS0 im ADMUX-Register. Die zu messende Spannung muss im Bereich zwischen &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AREF&#039;&#039;&#039; (egal ob intern oder extern) liegen. &lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; kann in zwei verschiedenen Betriebsarten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
; Einfache Wandlung (Single Conversion) : In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestoßen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
; Frei laufend (Free Running) : In dieser Betriebsart erfasst der Wandler permanent die anliegende Spannung und schreibt diese in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Die Register des ADC ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; verfügt über eigene Register. Im Folgenden die Registerbeschreibung eines  ATMega16, welcher über 8 ADC-Kanäle verfügt. Die Register unterscheiden sich jedoch nicht erheblich von denen anderer AVRs (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister A.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den &#039;&#039;&#039;ADC&#039;&#039;&#039; verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADEN&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able)&lt;br /&gt;
:Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren. Wenn das Bit nicht gesetzt ist, können die Pins wie normale I/O-Pins verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADSC&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion)&lt;br /&gt;
:Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.&lt;br /&gt;
:Wenn das Bit nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.&lt;br /&gt;
:Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADFR&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;F&#039;&#039;&#039;ree &#039;&#039;&#039;R&#039;&#039;&#039;un select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Ist das Bit auf 1 gesetzt arbeitet der ADC im Freerunning Modus. Dabe iwird das Datenregister permanent aktualisiert. Ist das Bit hingegen auf 0 gesetzt macht der ADC nur eine Single Conversion. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIF&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag)&lt;br /&gt;
:Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt, sobald eine Umwandlung erfolgt ist und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert wurde. Das Bit wird bei lesendem Zugriff auf &#039;&#039;&#039;ADC(L,H)&#039;&#039;&#039; automatisch (d.h. durch die Hardware) gelöscht.&lt;br /&gt;
:Wenn das &#039;&#039;&#039;ADIE&#039;&#039;&#039; Bit sowie das &#039;&#039;&#039;I-Bit&#039;&#039;&#039; im AVR &#039;&#039;&#039;Statusregister&#039;&#039;&#039; gesetzt ist, wird der &#039;&#039;&#039;ADC Interrupt&#039;&#039;&#039; ausgelöst und die Interrupt-Behandlungsroutine aufgerufen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn die Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 in das Register geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIE&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;, dann wird der &#039;&#039;&#039; ADC-Interrupt&#039;&#039;&#039; aktiviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADPS2...ADPS0&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;P&#039;&#039;&#039;rescaler &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
:Der &#039;&#039;&#039;ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz ergibt.&lt;br /&gt;
:Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
TF_{min}=\frac{CLK}{200\,\mathrm{kHz}}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50\,\mathrm{kHz}}=\frac{4000000}{50000}=\mathbf{80}&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im Interesse der schnelleren Wandlungszeit werden wir hier den Faktor 32 einstellen.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCL&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ADCH&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC &#039;&#039;&#039; Data Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn eine Umwandlung abgeschlossen ist, befindet sich der gemessene Wert in&lt;br /&gt;
diesen beiden Registern. Von &#039;&#039;&#039;ADCH&#039;&#039;&#039; werden nur die beiden niederwertigsten Bits verwendet. Es müssen immer beide Register ausgelesen werden, und zwar immer &#039;&#039;&#039;in der Reihenfolge: ADCL, ADCH&#039;&#039;&#039;. &lt;br /&gt;
Der effektive Messwert ergibt sich dann zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCL;       // mit uint16_t x&lt;br /&gt;
x += (ADCH&amp;lt;&amp;lt;8); // in zwei Zeilen (LSB/MSB-Reihenfolge und&lt;br /&gt;
                // C-Operatorpriorität sichergestellt)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADMUX&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Mu&#039;&#039;&#039;ltiple&#039;&#039;&#039;x&#039;&#039;&#039;er Select Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Register wird der zu messende Kanal ausgewählt. Beim 90S8535&lt;br /&gt;
kann jeder Pin von Port A als &#039;&#039;&#039;ADC&#039;&#039;&#039;-Eingang verwendet werden (=8 Kanäle).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADLAR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX4&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX3&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;REFS1...REFS0&#039;&#039;&#039; (&#039;&#039;&#039;Ref&#039;&#039;&#039;erence&#039;&#039;&#039;S&#039;&#039;&#039;election Bits)&lt;br /&gt;
:Mit diesen Bits kann die Referenzspannung eingestellt werden:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Referenzspanung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Externes AREF&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | AVCC als Referenz&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Interne 2,56 Volt&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADLAR&#039;&#039;&#039; (&#039;&#039;&#039;ADC &#039;&#039;&#039; &#039;&#039;&#039;L&#039;&#039;&#039;eft &#039;&#039;&#039;A&#039;&#039;&#039;djust &#039;&#039;&#039;R&#039;&#039;&#039;esult)&lt;br /&gt;
:Das ADLAR Bit verändert das Aussehen des Ergebnisses der AD-Wandlung. Bei einer logischen 1 wird das Ergebnis linksbündig ausgegeben, bei einer 0 rechtsündig. Eine Änderung in diesem Bit beeinflusst das Ergebnis sofort, ganz egal ob bereits eine Wandlung läuft.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX4...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 5 Bits wird der zu messende Kanal bestimmt. Wenn man einen einfachen 1-kanaligen ADC verwendet wird einfach die entsprechende Pinnummer des Ports in die Bits 0...2 eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
:Eine Empfehlung ist deswegen diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren des ADC ====&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039; ADC&#039;&#039;&#039; zu aktivieren, müssen wir das &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bit im &#039;&#039;&#039;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
Ein kleines Beispiel für den &amp;quot;single conversion&amp;quot;-Mode bei einem ATmega169 und Nutzung der internen Referenzspannung (beim &#039;169 1,1V bei anderen AVRs auch 2,56V). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler einstellen. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1023 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint16_t ReadChannel(uint8_t mux)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
&lt;br /&gt;
  ADMUX = mux;                      // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler &lt;br /&gt;
                               // setzen auf 8 (1) und ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  /* nach Aktivieren des ADC wird ein &amp;quot;Dummy-Readout&amp;quot; empfohlen, man liest&lt;br /&gt;
     also einen Wert und verwirft diesen, um den ADC &amp;quot;warmlaufen zu lassen&amp;quot; */&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
     ;     // auf Abschluss der Konvertierung warten &lt;br /&gt;
  }&lt;br /&gt;
  result = ADCW;  // ADCW muss einmal gelesen werden,&lt;br /&gt;
                  // sonst wird Ergebnis der nächsten Wandlung&lt;br /&gt;
                  // nicht übernommen.&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  result = 0; &lt;br /&gt;
  for( i=0; i&amp;lt;4; i++ )&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);            // eine Wandlung &amp;quot;single conversion&amp;quot;&lt;br /&gt;
    while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
      ;   // auf Abschluss der Konvertierung warten&lt;br /&gt;
    }&lt;br /&gt;
    result += ADCW;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Beispielaufrufe: */&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
&lt;br /&gt;
  adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -&amp;gt; Channel 0 */&lt;br /&gt;
  ...&lt;br /&gt;
  adcval = ReadChannel(2); /* MUX-Bits auf 0b0010 -&amp;gt; Channel 2 */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekennzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Das Löschen des ADIF-Flags sollte, &#039;&#039;&#039;entgegen&#039;&#039;&#039; der [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_intbits FAQ], mit&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADIF);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
erfolgen. Die Methode in der FAQ eignet sich nur für Register in denen &#039;&#039;&#039;nur&#039;&#039;&#039; Interrupt-Flags stehen.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Analog-Digital-Wandlung ohne internen ADC ===&lt;br /&gt;
&lt;br /&gt;
==== Messen eines Widerstandes ====&lt;br /&gt;
&lt;br /&gt;
Analoge Werte lassen sich ohne Analog-Digital-Wandler auch indirekt ermitteln. Im Folgenden wird die Messung des an einem Potentiometer eingestellten Widerstands anhand der Ladekurve eines Kondensators erläutert. Bei dieser Methode wird nur ein Portpin benötigt, ein Analog-Digital-Wandler oder Analog-Comparator ist nicht erforderlich. Es wird dazu ein Kondensator und der Widerstand (das Potentiometer) in Reihe zwischen Vorsorgungsspannung und Masse/GND geschaltet (sogen. RC-Netzwerk). Zusätzlich wird eine Verbindung der Leitung zwischen Kondensator und Potentiometer zu einem Portpin des Controllers hergestellt. Die folgende Abbildung verdeutlicht die erforderliche Schaltung. &lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wird der Portpin des Controllers auf Ausgang konfiguriert (im Beispiel &#039;&#039;DDRD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) und dieser Ausgang auf Logisch 1 (&amp;quot;High&amp;quot;, &#039;&#039;PORTD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) geschaltet, liegt an beiden &amp;quot;Platten&amp;quot; des Kondensators das gleiche Potential &#039;&#039;&#039;VCC&#039;&#039;&#039; an und der Kondensator somit entladen. (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so, da an beiden Seiten des Kondensators das gleiche Potential anliegt und somit eine Potentialdifferenz von 0V besteht =&amp;gt; Kondensator ist entladen).&lt;br /&gt;
&lt;br /&gt;
Nach einer gewissen Zeit ist der Kondensator entladen und der Portpin wird als Eingang konfiguriert (&#039;&#039;DDRD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2); PORTD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2)&#039;&#039;), wodurch dieser hochohmig wird. Der Status des Eingangspin (in PIND) ist Logisch 1 (High). Der Kondensator lädt sich jetzt über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), wird das Eingangssignal als LOW erkannt (Bit in PIND wird 0). Die Zeitspanne zwischen der Umschaltung von Entladung auf Aufladung und dem Wechsel des Eingangssignals von High auf Low ist ein Maß für den am Potentiometer eingestellten Widerstand. Zur Zeitmessung kann einer der im Controller vorhandenen Timer genutzt werden. Der 220 Ohm Widerstand dient dem Schutz des Controllers. Es würde sonst bei Maximaleinstellung des Potentionmeters (hier 0 Ohm) ein zu hoher Strom fließen, der die Ausgangsstufe des Controllers zerstört. &lt;br /&gt;
&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B: 0...100 % oder so) umzurechnen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Link 404 =&amp;gt; auskommentiert, mthomas 9.2.2008 &lt;br /&gt;
Ein Beispielprogramm findet sich auf [http://www.mypage.bluewin.ch/ch_schifferle/ Christian Schifferles Web-Seite] im Archiv &#039;&#039;ATMEL.ZIP&#039;&#039;, welches unter den Titel &#039;&#039;Tutorial &amp;quot;Programmieren mit C für Atmel Mikrocontroller&#039;&#039; heruntergeladen werden kann. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ADC über Komparator ====&lt;br /&gt;
&lt;br /&gt;
Es gibt einen weiteren Weg, eine analoge Spannung mit Hilfe des&lt;br /&gt;
Komparators, welcher in fast jedem AVR integriert ist, zu messen. Siehe dazu&lt;br /&gt;
auch die Application Note AVR400 von Atmel.&lt;br /&gt;
&lt;br /&gt;
Dabei wird das zu messende Signal auf den invertierenden Eingang&lt;br /&gt;
des Komparators geführt. Zusätzlich wird ein Referenzsignal an den nicht&lt;br /&gt;
invertierenden Eingang des Komparators angeschlossen. Das Referenzsignal wird&lt;br /&gt;
hier auch wieder über ein RC-Glied erzeugt, allerdings mit festen Werten für R&lt;br /&gt;
und C.&lt;br /&gt;
&lt;br /&gt;
[[Image:ADC ueber Komparator.gif]]&lt;br /&gt;
&lt;br /&gt;
Das Prinzip der Messung ist nun dem vorhergehenden recht&lt;br /&gt;
ähnlich. Durch Anlegen eines LOW-Pegels an Pin 2 wird der Kondensator zuerst&lt;br /&gt;
einmal entladen. Auch hier muss darauf geachtet werden, dass der Entladevorgang&lt;br /&gt;
genügend lang dauert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Nun wird Pin 2 auf HIGH gelegt. Der Kondensator wird geladen. Wenn die Spannung&lt;br /&gt;
über dem Kondensator die am Eingangspin anliegende Spannung erreicht hat&lt;br /&gt;
schaltet der Komparator durch. Die Zeit, welche benötigt wird, um den&lt;br /&gt;
Kondensator zu laden kann nun auch wieder als Maß für die Spannung an Pin 1&lt;br /&gt;
herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Ich habe es mir gespart, diese Schaltung auch aufzubauen und&lt;br /&gt;
zwar aus mehreren Gründen:&lt;br /&gt;
&lt;br /&gt;
# 3 Pins notwendig.&lt;br /&gt;
# Genauigkeit vergleichbar mit einfacherer Lösung.&lt;br /&gt;
# War einfach zu faul.&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung liegt allerdings darin, dass damit&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines Digital-Analog-Konverters (&#039;&#039;&#039;DAC&#039;&#039;&#039;) können wir nun auch Analogsignale ausgeben. Es gibt hier mehrere Verfahren. &amp;lt;!-- Wenn wir beim ADC die Möglichkeit haben, mit externen Komponenten zu operieren, müssen wir bei der DAC-Wandlung mit dem auskommen, was der Controller selber zu bieten hat. --mt: hmm, richtig? verstaendlich? redundant? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DAC über mehrere digitale Ausgänge ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir an den Ausgängen des Controllers ein entsprechendes&lt;br /&gt;
Widerstandsnetzwerk aufbauen haben wir die Möglichkeit, durch die Ansteuerung&lt;br /&gt;
der Ausgänge über den Widerständen einen Addierer aufzubauen, mit dessen&lt;br /&gt;
Hilfe wir eine dem Zahlenwert proportionale Spannung erzeugen können. Das&lt;br /&gt;
Schaltbild dazu kann etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
[[Image:DAC R2R.gif]]&lt;br /&gt;
&lt;br /&gt;
Es sollten selbstverständlich möglichst genaue Widerstände verwendet&lt;br /&gt;
werden, also nicht unbedingt solche mit einer Toleranz von 10% oder mehr.&lt;br /&gt;
Weiterhin empfiehlt es sich, je nach Anwendung den Ausgangsstrom über einen&lt;br /&gt;
Operationsverstärker zu verstärken.&lt;br /&gt;
&lt;br /&gt;
=== PWM (Pulsweitenmodulation) ===&lt;br /&gt;
&lt;br /&gt;
Wir kommen nun zu einem Thema, welches in aller Munde ist, aber viele&lt;br /&gt;
Anwender verstehen nicht ganz, wie &#039;&#039;&#039; PWM&#039;&#039;&#039; eigentlich funktioniert.&lt;br /&gt;
&lt;br /&gt;
Wie wir alle wissen, ist ein Mikrocontroller ein rein digitales Bauteil.&lt;br /&gt;
Definieren wir einen Pin als Ausgang, dann können wir diesen Ausgang entweder&lt;br /&gt;
auf HIGH setzen, worauf am Ausgang die Versorgungsspannung &#039;&#039;&#039; Vcc&#039;&#039;&#039; anliegt, oder aber wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt. Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen HIGH und LOW umschalten? - Richtig, wir erhalten eine Rechteckspannung, wie die folgende Abbildung zeigt:&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 1.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Rechteckspannung hat nun einen arithmetischen Mittelwert, &lt;br /&gt;
der je nach Pulsbreite kleiner oder größer ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 2.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun diese pulsierende Ausgangsspannung noch über ein RC-Glied filtern/&amp;quot;glätten&amp;quot;, dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVRs können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. &lt;br /&gt;
Dazu dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt, weshalb ein Blick ins entsprechende Datenblatt dringend angeraten wird.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren, müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| PWM-Modus des Timers ist nicht aktiv.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Timer/Counter zählt nun permanent von 0 bis zur Obergrenze&lt;br /&gt;
und wieder zurück, er wird also als sogenannter Auf-/Ab Zähler betrieben. &lt;br /&gt;
Die Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss mit den Bits &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; desselben&lt;br /&gt;
Registers die gewünschte Ausgabeart des Signals definiert werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Nicht invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Hochzählen und gesetzt beim&lt;br /&gt;
Herunterzählen.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Herunterzählen und gesetzt beim&lt;br /&gt;
Hochzählen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl, um beispielsweise den Timer/Counter als&lt;br /&gt;
nicht invertierenden 10-Bit PWM zu verwenden, heißt dann:&lt;br /&gt;
&lt;br /&gt;
alte Schreibweise (PWMxx wird nicht mehr akzeptiert)&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;PWM11)|(1&amp;lt;&amp;lt;PWM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
neue Schreibweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;WGM11)|(1&amp;lt;&amp;lt;WGM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Timer/Counter überhaupt läuft, müssen wir im Control&lt;br /&gt;
Register B &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; noch den gewünschten Takt (Vorteiler) einstellen und&lt;br /&gt;
somit auch die Frequenz des &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signals bestimmen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stop. Der Timer/Counter wird gestoppt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin 1, negative Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin 1, positive Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Also um einen Takt von CK / 1024 zu generieren, verwenden wir&lt;br /&gt;
folgenden Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1B = (1&amp;lt;&amp;lt;CS12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt muss nur noch der Vergleichswert festgelegt werden. Diesen&lt;br /&gt;
schreiben wir in das 16-Bit Timer/Counter Output Compare Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem Vergleichswert und dem generierten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal aufzeigen.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
Ach ja, fast hätte ich&#039;s vergessen. Das generierte &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal&lt;br /&gt;
wird am Output Compare Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; des Timers ausgegeben und leider können wir&lt;br /&gt;
deshalb auch beim AT90S2313 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren. Andere AVR-Typen verfügen über bis zu vier PWM-Ausgänge. Zu beachten ist außerdem, das wenn der OC Pin aktiviert ist, er nichtmehr wie üblich funktioniert und z.B. nicht einfach über PINx ausgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Ein Programm, dass an einem ATMega8 den Fast-PWM Modus verwendet, den Modus 14, könnte so aussehen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // OC1A auf Ausgang&lt;br /&gt;
  DDRB = (1 &amp;lt;&amp;lt; PB1 );&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // Timer 1 einstellen&lt;br /&gt;
  //  &lt;br /&gt;
  // Modus 14:&lt;br /&gt;
  //    Fast PWM, Top von ICR1&lt;br /&gt;
  //&lt;br /&gt;
  //     WGM13    WGM12   WGM11    WGM10&lt;br /&gt;
  //      1        1       1        0&lt;br /&gt;
  //&lt;br /&gt;
  //    Timer Vorteiler: 1&lt;br /&gt;
  //     CS12     CS11    CS10&lt;br /&gt;
  //       0        0       1&lt;br /&gt;
  //&lt;br /&gt;
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match&lt;br /&gt;
  //     COM1A1   COM1A0&lt;br /&gt;
  //       1        0&lt;br /&gt;
 &lt;br /&gt;
  TCCR1A = (1&amp;lt;&amp;lt;COM1A1) | (1&amp;lt;&amp;lt;WGM11);&lt;br /&gt;
  TCCR1B = (1&amp;lt;&amp;lt;WGM13) | (1&amp;lt;&amp;lt;WGM12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  //  den Endwert (TOP) für den Zähler setzen&lt;br /&gt;
  //  der Zähler zählt bis zu diesem Wert&lt;br /&gt;
&lt;br /&gt;
  ICR1 = 0x6FFF;&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  // der Compare Wert&lt;br /&gt;
  // Wenn der Zähler diesen Wert erreicht, wird mit&lt;br /&gt;
  // obiger Konfiguration der OC1A Ausgang abgeschaltet&lt;br /&gt;
  // Sobald der Zähler wieder bei 0 startet, wird der&lt;br /&gt;
  // Ausgang wieder auf 1 gesetzt&lt;br /&gt;
  //&lt;br /&gt;
  // Durch Verändern dieses Wertes, werden die unterschiedlichen&lt;br /&gt;
  // PWM Werte eingestellt.&lt;br /&gt;
&lt;br /&gt;
  OCR1A = 0x3FFF;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  while( 1 )&lt;br /&gt;
    ;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für Details der PWM Möglichkeiten, muß immer das jeweilge Datenblatt des Prozessors konsultiert werden, da sich die unterschiedlichen Prozessoren in ihren Möglichkeiten doch stark unterscheiden. Auch muß man aufpassen, welches zu setzende Bit in welchem Register sind. Auch hier kann es sein, dass gleichnamige Konfigurationsbits in unterschiedlichen Konfigurationsregistern (je nach konkretem Prozessortyp) sitzen.&lt;br /&gt;
&lt;br /&gt;
= LCD-Ansteuerung =&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung an der LCD-Controller-Platine ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; (ist in manchen Unterlagen auch &#039;&#039;&#039;EN&#039;&#039;&#039;  für &#039;&#039;Enable&#039;&#039; abgekürzt) benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert, ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitetet hat (diese Methode u.a. in der LCD-Library von Peter Fleury verwendet). Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt da wir wissen, welche Anschlüsse das LCDs benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin #-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin-µC&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND oder Poti (siehe oben)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD4 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD5 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD0 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD1 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD2 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD3 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man die Steuerleitungen EN und RS auf Pins an einem anderen Port legen möchte, kann man so wie in diesem [http://www.mikrocontroller.net/topic/88543#751982 Forumsbeitrag] vorgehen.&lt;br /&gt;
&lt;br /&gt;
Ok, alles ist verbunden, wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display? &lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
void lcd_data(unsigned char temp1);&lt;br /&gt;
void lcd_string(char *data);&lt;br /&gt;
void lcd_command(unsigned char temp1);&lt;br /&gt;
void lcd_enable(void);&lt;br /&gt;
void lcd_init(void);&lt;br /&gt;
void lcd_home(void);&lt;br /&gt;
void lcd_clear(void);&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y);&lt;br /&gt;
&lt;br /&gt;
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
&lt;br /&gt;
// LCD Befehle&lt;br /&gt;
&lt;br /&gt;
#define CLEAR_DISPLAY 0x01&lt;br /&gt;
#define CURSOR_HOME   0x02&lt;br /&gt;
&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
&lt;br /&gt;
#define LCD_PORT      PORTD&lt;br /&gt;
#define LCD_DDR       DDRD&lt;br /&gt;
#define LCD_RS        PD4&lt;br /&gt;
#define LCD_EN        PD5&lt;br /&gt;
// DB4 bis DB7 des LCD sind mit PD0 bis PD3 des AVR verbunden&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// sendet ein Datenbyte an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_data(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_RS);        // RS auf 1 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_command(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);        // RS auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;              // oberes Nibble holen&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;            // maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;            // unteres Nibble holen und maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// erzeugt den Enable-Puls&lt;br /&gt;
void lcd_enable(void)&lt;br /&gt;
{&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
    _delay_us(1);                   // kurze Pause&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/80900&lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Initialisierung: &lt;br /&gt;
// Muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
void lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
   LCD_DDR = LCD_DDR | 0x0F | (1&amp;lt;&amp;lt;LCD_RS) | (1&amp;lt;&amp;lt;LCD_EN);   // Port auf Ausgang schalten&lt;br /&gt;
&lt;br /&gt;
   // muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(15);&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x03;            &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);      // RS auf 0&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4 Bit Modus aktivieren &lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x02;&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4Bit / 2 Zeilen / 5x7&lt;br /&gt;
   lcd_command(0x28);&lt;br /&gt;
    &lt;br /&gt;
   // Display ein / Cursor aus / kein Blinken&lt;br /&gt;
   lcd_command(0x0C); &lt;br /&gt;
 &lt;br /&gt;
   // inkrement / kein Scrollen&lt;br /&gt;
   lcd_command(0x06);&lt;br /&gt;
&lt;br /&gt;
   lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
&lt;br /&gt;
void lcd_clear(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CLEAR_DISPLAY);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
&lt;br /&gt;
void lcd_home(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CURSOR_HOME);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)&lt;br /&gt;
&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t tmp;&lt;br /&gt;
&lt;br /&gt;
  switch (y) {&lt;br /&gt;
    case 1: tmp=0x80+0x00+x; break;    // 1. Zeile&lt;br /&gt;
    case 2: tmp=0x80+0x40+x; break;    // 2. Zeile&lt;br /&gt;
    case 3: tmp=0x80+0x10+x; break;    // 3. Zeile&lt;br /&gt;
    case 4: tmp=0x80+0x50+x; break;    // 4. Zeile&lt;br /&gt;
  }&lt;br /&gt;
  lcd_command(tmp);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_string(char *data)&lt;br /&gt;
{&lt;br /&gt;
    while(*data) {&lt;br /&gt;
        lcd_data(*data);&lt;br /&gt;
        data++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches die Funktionen benutzt, sieht zb. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen&lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    lcd_data(&#039;T&#039;);&lt;br /&gt;
    lcd_data(&#039;e&#039;);&lt;br /&gt;
    lcd_data(&#039;s&#039;);&lt;br /&gt;
    lcd_data(&#039;t&#039;);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
&lt;br /&gt;
    lcd_string(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist dabei, dass die Optimierung bei der Compilierung eingeschaltet ist, sonst stimmen die Zeiten der Funktionen _delay_us() und _delay_ms() nicht und der Code wird wesentlich länger (Siehe Dokumentation der libc im WinAVR).&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches eine Variable ausgibt, sieht zb. so aus.&lt;br /&gt;
Mittels der itoa() Funktion (itoa = &amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;nteger &amp;lt;b&amp;gt;To&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;A&amp;lt;/b&amp;gt;scii ) wird von einem Zahlenwert eine textuelle Repräsentierung ermittelt (sprich: ein String erzeugt) und dieser String mit der bereits vorhandenen Funktion lcd_string ausgegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen &lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Beispiel&lt;br /&gt;
int variable = 42;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe des Zeichens dessen ASCII-Code gleich dem Variablenwert ist&lt;br /&gt;
    // (Im Beispiel entspricht der ASCII-Code 42 dem Zeichen *)&lt;br /&gt;
    // http://www.code-knacker.de/ascii.htm&lt;br /&gt;
    lcd_data(variable);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
 &lt;br /&gt;
    // Ausgabe der Variable als Text in dezimaler Schreibweise&lt;br /&gt;
    {&lt;br /&gt;
       // ... umwandeln siehe FAQ Artikel bei www.mikrocontroller.net&lt;br /&gt;
       // WinAVR hat eine itoa()-Funktion, das erfordert obiges #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
       char Buffer[20]; // in diesem {} lokal&lt;br /&gt;
       itoa( variable, Buffer, 10 ); &lt;br /&gt;
&lt;br /&gt;
       // ... ausgeben  &lt;br /&gt;
       lcd_string( Buffer );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einrichten eines Projekts muss man zu der Datei mit dem Hauptprogramm auch die Datei lcd-routines.c in das Projekt aufnehmen. Dies geschieht beim AVR Studio unter Source Files im Fenster AVR GCC oder bei WinAVR im Makefile (z.B. durch SRC += lcd-routines.c).&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVRs sind für viele Steuerungsaufgaben zu schnell. Wenn wir beispielsweise eine LED oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die CPU-Frequenz verwenden, da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, Vorgänge in Zeitabständen durchzuführen, die geringer als die Taktfrequenz des Controllers sind. Selbstverständlich sollte die resultierende Frequenz auch noch möglichst genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist ganz einfach ein bestimmtes Register im µC, das völlig ohne Zutun des Programms, also per Hardware, hochgezählt wird. Das alleine wäre noch nicht allzu nützlich, wenn nicht dieses Hardwareregister bei bestimmten Zählerständen einen Interrupt auslösen könnte. Ein solches Ereignis ist der Overflow: Da die Bitbreite des Registers beschränkt ist, kommt es natürlich auch vor, dass der Zähler so hoch zählt, dass der nächste Zählerstand mit dieser Bitbreite nicht mehr darstellbar ist und der Zähler wieder auf 0 zurückgesetzt wird. Dieses Ereignis nennt man den Overflow und es ist möglich an dieses Ereignis einen Interrupt zu koppeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Alternativvorschlag mthomas &lt;br /&gt;
Jeder Timer verfügt über ein Zählerregister im Mikrocontroller, das automatisch und ohne Zutun des Programms von der Hardware weitergezählt wird. In einem einfachen Anwendungsfall stellt man den Timer auf eine Zählgeschwindigkeit (Frequenz) und kann dann anhand des Zählerstands ermitteln, wie viel Zeit vergangen ist. Das eigentlich Nützliche an Timern ist jedoch, dass man  bestimmte Zählerstände mit Interrupts verknüpfen kann, so dass der Controller beim Auftreten automatisch eine vom Anwender geschriebene Routine aufruft. Eines dieser möglichen Ereignis ist der Overflow ((Zähler-)Überlauf), der dann auftritt, wenn der Wert des Zählerregisters (Timer/Counter-Register) den maximal möglichen Wert überschreitet. Der Maximalwert wird durch die Bitbreite des Zählerregisters bestimmt (z.B. 255 bei 8-Bit Timern). Beim Überlauf/Overflow wird der Zähler durch die Hardware auf 0 zurückgesetzt und die Zählung beginnt von neuem. Wurde vorher der Overflow-Interrupt für den Timer aktiviert (im Timer Control Register) unterbricht der Controller automatisch die Ausführung des Hauptprogramms und verzweigt in die Interrupt-Routine des Anwenders.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auf den AT90S2313. Für andere Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer Auflösung von 65536. Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz, der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
== Der Vorteiler (Prescaler) ==&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler dient dazu, den CPU-Takt vorerst um einen einstellbaren Faktor zu reduzieren. Die so geteilte Frequenz wird den Eingängen der Timer zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024 einstellen, wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca. 4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw. Zählregister (TCNTx) mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle verfügen über mindestens einen, teilweise sogar zwei, 8-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird z.B bei AT90S2313 über folgende Register angesprochen (bei anderen Typen weitestgehend analog):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    TCCR0 |= (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst und die daran gebundene Interrupt-Routine abgearbeitet. Das TOV Flag &lt;br /&gt;
lässt sich durch das Hineinschreiben einer 1 und nicht wie erwartet einer 0 wieder zurücksetzen.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen außer den 8-Bit Timern auch 16-Bit Timer. Die 16-Bit Timer/Counter sind etwas komplexer aufgebaut als die 8-Bit Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM13&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM12 (CTC1)&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorteiler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt, und C der voreingestellte Vergleichswert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin T1, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin T1, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T1 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T1 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0, bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;TCNT1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;OCR1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;OCR1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;OCR1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;OCR1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäß folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]] [[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Ausserdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr. Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen kleiner 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4us warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.6 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t x;&lt;br /&gt;
&lt;br /&gt;
    x = 10;&lt;br /&gt;
&lt;br /&gt;
    while (x &amp;gt;= 0)&lt;br /&gt;
    {&lt;br /&gt;
      // tu was&lt;br /&gt;
&lt;br /&gt;
      x--;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog? ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Einige Controller haben einen eigenen Watchdog Oszillator, z.B. der Tiny2313 mit 128kHz. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde, beginnt der Counter von 0 an hochzuzählen. &lt;br /&gt;
Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern, müssen wir den Watchdog regelmäßig wieder neu starten bzw. rücksetzen (Watchdog Reset). &lt;br /&gt;
Dies sollte innerhalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern, muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten. Es müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. &lt;br /&gt;
Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist, wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei &#039;&#039;wdt.h&#039;&#039; (&#039;&#039;#include &amp;lt;avr/wdt.h&amp;gt;&#039;&#039;) in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch gestartet wird. &lt;br /&gt;
Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. &lt;br /&gt;
Falls ein anderer Wert gewünscht ist, so kann dies im Makfile in den Linker-Optionen eingetragen werden. &lt;br /&gt;
Dazu muss in der Zeile LDFLAGS folgende Option angefügt werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert&lt;br /&gt;
:Mögliche Timeoutwerte:&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Konstante&lt;br /&gt;
! Wert&lt;br /&gt;
! TimeOut&lt;br /&gt;
|- &lt;br /&gt;
| WDTO_15MS   &lt;br /&gt;
| 0&lt;br /&gt;
| 15 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_30MS   &lt;br /&gt;
| 1&lt;br /&gt;
| 30 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_60MS   &lt;br /&gt;
| 2&lt;br /&gt;
| 60 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_120MS   &lt;br /&gt;
| 3&lt;br /&gt;
| 120 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_250MS   &lt;br /&gt;
| 4&lt;br /&gt;
| 250 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_500MS   &lt;br /&gt;
| 5&lt;br /&gt;
| 500 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_1S   &lt;br /&gt;
| 6&lt;br /&gt;
| 1 S&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_2S   &lt;br /&gt;
| 7&lt;br /&gt;
| 2s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
== Watchdog-Anwendungshinweise ==&lt;br /&gt;
&lt;br /&gt;
Ob nun der Watchdog als Schutzfunktion überhaupt verwendet werden soll, hängt stark von der Anwendung, der genutzten Peripherie und dem Umfang und der Qualitätssicherung des Codes ab. Will man sicher gehen, dass ein Programm sich nicht in einer Endlosschleife verfängt, ist der Wachdog das geeignete Mittel dies zu verhindern. Weiterhin kann bei geschickter Programmierung der Watchdog dazu genutzt werden, bestimmte Stromsparfunktionen zu implementieren. Bei einigen neueren AVRs (z.B. dem ATTiny13) kann der Watchdog auch direkt als Timer genutzt werden, der den Controller aus einem Schlafmodus aufweckt. Auch dies kann im &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register eingestellt werden. Außerdem bietet der WD die einzige Möglichkeit einen beabsichtigten System-Reset (ein &amp;quot;richtiger Reset&amp;quot;, kein &amp;quot;jmp 0x0000&amp;quot;) ohne externe Beschaltung auszulösen, was z.B. bei der Implementierung eines Bootloaders nützlich ist. Bei bestimmten Anwendungen kann die Nutzung des WD als &amp;quot;ultimative Deadlock-Sicherung für nicht bedachte Zustände&amp;quot; natürlich immer als zusätzliche Sicherung dienen. &lt;br /&gt;
&lt;br /&gt;
Es besteht die Möglichkeit herauszufinden, ob ein Reset durch den Watchdog ausgelöst wurde (beim ATmega16 z.B. Bit WDRF in MCUCSR). Diese Information sollte auch genutzt werden, falls ein WD-Reset in der Anwendung nicht planmäßig implementiert wurde. Zum Beispiel kann man eine LED an einen freien Pin hängen, die nur bei einem Reset durch den WD aufleuchtet oder aber das &amp;quot;Ereignis WD-Reset&amp;quot; im internen EEPROM des AVR absichern, um die Information später z.B. über UART oder ein Display auszugeben (oder einfach den EEPROM-Inhalt über die ISP/JTAG-Schnittstelle auslesen).&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/77273#642501 Bug in ATtiny2313?]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt der Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft. Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe Abschnitt (TODO: verlinken) im Anhang.&lt;br /&gt;
&amp;lt;!--Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. vgl. [[AVR-GCC-Tutorial#Anhang|Anhang]])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.B. &#039;&#039;void TIMER0_OVF_vect(void)...&#039;&#039;). Der Unterschied im Vergleich zu ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben.  &amp;lt;!--Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)), als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur &lt;br /&gt;
in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechnungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechnungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0, PORTA ist danach 0b00000001. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-oder-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8 (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten &#039;&#039;Sleep-Modes&#039;&#039; (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_enable()&#039;&#039;&#039;&lt;br /&gt;
:aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_cpu()&#039;&#039;&#039;&lt;br /&gt;
: Versetzt den Controller in den Schlafmodus (sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt)&lt;br /&gt;
* &#039;&#039;&#039;sleep_disable()&#039;&#039;&#039;&lt;br /&gt;
:deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts.&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;allozieren&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich, um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory, bei AVR Controllern mit mehr als 64kB Flash auch elpm). Die Standard-Laufzeitbibliothek des avr-gcc (die avr-libc) stellt diese Funktionen nach Einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Lokale Variablen (eigentlich Konstanten) innerhalb von Funktionen können ebenfalls im Programmspeicher abgelegt werden. Dazu ist bei der Definition jedoch ein &#039;&#039;static&#039;&#039; voranzustellen, da solche &amp;quot;Variablen&amp;quot; nicht auf dem Stack bzw. (bei Optimierung) in Registern verwaltet werden können. Der Compiler &amp;quot;wirft&amp;quot; eine Warnung falls static fehlt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray2 };&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  static /*const*/ uint8_t pgmTestByteLocal PROGMEM = 0x55;&lt;br /&gt;
  static /*const*/ char pgmTestStringLocal[] PROGMEM = &amp;quot;im Flash&amp;quot;;&lt;br /&gt;
  // so nicht (static fehlt): char pgmTestStringLocalFalsch [] PROGMEM = &amp;quot;so nicht&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 // ...&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
    //...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte....&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWort);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;groß&amp;quot; (Stand avr-gcc 3.4.x). Damit ist der mögliche Speicherbereich für &amp;quot;Flash-Konstanten&amp;quot; auf 64kB begrenzt.  &amp;lt;!-- Einige avr-libc/pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher) (intern via Assembler Anweisung ELPM). Die Initialisierungswerde des Speicherinhalts jenseits der 64kB-Marke müssen dann jedoch auf anderem Weg angelegt werden (nicht PROGMEM, evtl. eigene Section und Linker-Optionen - TODO) /// alt - und nicht ganz korrekt: (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.)--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray enthält nun die Startadresse des Byte-Arrays pgmFooByteArray1&lt;br /&gt;
    // Allerdings würde ein direkter Zugriff mit diesem Pointer (z.B. temp=*ptrToArray)&lt;br /&gt;
    // &#039;&#039;&#039;nicht&#039;&#039;&#039; den Inhalt von pgmFooByteArray1[0] liefern, sondern von einer Speicherstelle&lt;br /&gt;
    // im &#039;&#039;&#039;RAM&#039;&#039;&#039;, die die gleiche Adresse hat wie pgmFooByteArray1[0]&lt;br /&gt;
    // Daher muss nun die Funktion pgm_read_byte() benutzt werden, die die in ptrToArray&lt;br /&gt;
    // enthaltene Adresse benutzt und auf das Flash zugreift.&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray enthält nun die Adresse des ersten Elements des Byte-Arrays pgmFooByteArray2&lt;br /&gt;
    // da im zweiten Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmFooByteArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Array aus Zeichenketten im Flash-Speicher ===&lt;br /&gt;
&lt;br /&gt;
Felder aus Zeichenketten im Flash-Speicher werden in zwei Schritten angelegt: Zuerst die einzelnen Elemente des Arrays und im Anschluss ein Array, in dem die Addressen der Zeichenketten abgelegt werden. Zum Auslesen wird zuerst die Adresse des i-ten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, auf das Element (die Zeichenkette) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const char str1[] PROGMEM = &amp;quot;first_A&amp;quot;;&lt;br /&gt;
const char str2[] PROGMEM = &amp;quot;second_A&amp;quot;;&lt;br /&gt;
const char str3[] PROGMEM = &amp;quot;third_A&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char *strarray1[] PROGMEM = {&lt;br /&gt;
	str1,&lt;br /&gt;
	str2,&lt;br /&gt;
	str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	int i, j, l;&lt;br /&gt;
	const char *pstrflash;&lt;br /&gt;
	char work[20], work2[20];&lt;br /&gt;
	// fuer Simulation: per volatile Optimierung verhindern, &lt;br /&gt;
	//                  da c nicht genutzt&lt;br /&gt;
	volatile char c;&lt;br /&gt;
	&lt;br /&gt;
	for ( i = 0; i &amp;lt; (sizeof(strarray1)/sizeof(strarray1[0]) ); i++ ) {&lt;br /&gt;
&lt;br /&gt;
		// setze Pointer auf die Addresse des i-ten Elements des&lt;br /&gt;
		// &amp;quot;Flash-Arrays&amp;quot; (str1, str2, ...)&lt;br /&gt;
		pstrflash = (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) );&lt;br /&gt;
		&lt;br /&gt;
		// kopiere den Inhalt der Zeichenkette von der&lt;br /&gt;
		// in pstrflash abgelegten Adresse in das work-Array&lt;br /&gt;
		// analog zu strcpy( work, strarray1[i]) wenn alles im RAM&lt;br /&gt;
		strcpy_P( work, pstrflash );&lt;br /&gt;
		// verkuerzt:&lt;br /&gt;
		strcpy_P( work2, (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) ) );&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
		// Zeichen-fuer-Zeichen&lt;br /&gt;
		l = strlen_P( pstrflash );&lt;br /&gt;
		for ( j=0; j &amp;lt; l; j++ ) {&lt;br /&gt;
			// analog zu c=strarray[i][j] wenn alles im RAM&lt;br /&gt;
			c = (char)( pgm_read_byte( pstrflash++ ) );&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	while (1) { ; }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: &amp;quot;How do I put an array of strings completely in ROM?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5)) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Vorsicht: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char textImFlashOK[] PROGMEM = &amp;quot;mit[]&amp;quot;; &lt;br /&gt;
// = Daten im &amp;quot;Flash&amp;quot;, textImFlashOK* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char* textImFlashProblem PROGMEM = &amp;quot;mit*&amp;quot;;&lt;br /&gt;
// Konflikt: Daten im BSS (lies: RAM), textImFlashFAIL* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der Initialisierungsstring von &amp;quot;textImFlashProblem&amp;quot; zu den Konstanten ans Ende des Programmcodes gelegt wird (BSS), von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte (und wird). Da der lesende Code (mittels pgm_read*) trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein weiters Problem des AVR-GCC (gesehen bei avr-gcc 3.4.1 und 3.4.2) bei der Anpassung an die Harvard-Architektur zu sein (konstanter Pointer auf variable Daten?!). Abhilfe (&amp;quot;Workaround&amp;quot;): Initialisierung bei Zeichenketten mit [] oder gleich im Code PSTR(&amp;quot;...&amp;quot;) nutzen.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden, ob es sich um eine Adresse im Flash oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind.&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die einen im Flash abgelegten String z.B. an eine UART ausgibt, würde dann so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void uart_puts_p(const char *text)&lt;br /&gt;
{&lt;br /&gt;
    char Zeichen;&lt;br /&gt;
&lt;br /&gt;
    while (Zeichen = pgm_read_byte(text))&lt;br /&gt;
    {   /* so lange, wie mittels pgm_read_byte ein Zeichen vom Flash gelesen&lt;br /&gt;
           werden konnte, welches nicht das &amp;quot;String-Endezeichen&amp;quot; darstellt */&lt;br /&gt;
&lt;br /&gt;
        /* Das gelesene Zeichen über die normalen Kanäle verschicken */&lt;br /&gt;
        uart_putc(Zeichen);&lt;br /&gt;
        text++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Flash in der Anwendung schreiben ===&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option (auch bekannt als Bootloader-Support) können Teile des Flash-Speichers auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. Bei wenigen &amp;quot;kleinen&amp;quot; AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm(Flash)- und Datenspeicher(RAM) auf. Der C-Standard und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiß&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, dass vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgegeben sind, die innerhalb weniger Taktzyklen aufeinanderfolgen müssen (&amp;quot;timed sequence&amp;quot;). Auch dies muss bei Nutzung der Funktionen aus der avr-libc/eeprom.h-Datei nicht selbst implementiert werden. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Siehe dazu folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt; // wird in aktuellen Versionen der avr-lib mit xx.h eingebunden&lt;br /&gt;
&lt;br /&gt;
// EEMEM wird bei aktuellen Versionen der avr-lib in eeprom.h definiert&lt;br /&gt;
// hier: definiere falls noch nicht bekannt (&amp;quot;alte&amp;quot; avr-libc)&lt;br /&gt;
#ifndef EEMEM&lt;br /&gt;
// alle Textstellen EEMEM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEMEM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
//...&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // &#039;Variablen&#039; eeFooByte geschrieben&lt;br /&gt;
//...&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z.B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    #define EEPROM_DEF 0xFF&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ( ( myByte = eeprom_read_byte(&amp;amp;eeFooByte) ) == EEPROM_DEF ) {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
//...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprüft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und möglicherweise noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,3);&lt;br /&gt;
&lt;br /&gt;
    /* dito etwas anschaulicher aber &amp;quot;unnütze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fließkommazahlen lassen sich recht praktisch über eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t i[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    uint8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEMEM;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
//...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklaration abgeleiteten EEPROM-Inhalt in eine Datei geschrieben werden (übliche Dateiendung: .eep, Daten im Intel Hex-Format). Damit können recht elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen (siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles). Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden (Write EEPROM), wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse (vgl. Datenblatt Abschnitt Fuse Bits) die vorherigen Daten (EESAVE programmed = 0), deren Position möglicherweise nicht mehr mit der Belegung im aktuellen Programm übereinstimmt oder den Standardwert nach &amp;quot;Chip Erase&amp;quot;: 0xFF (EESAVE unprogrammed = 1). Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Eine besondere Funktion des avr-gcc ist, dass mit entsprechenden Optionen im Makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Variable auf feste Adressen legen ===&lt;br /&gt;
&lt;br /&gt;
Gleich zu Beginn möchte ich darauf hinweisen, dass dieses Verfahren nur ein Workaround ist, mit dem man das Problem der anscheinend &amp;quot;zufälligen&amp;quot; Verteilung&lt;br /&gt;
der EEPROM-Variablen durch den Compiler etwas in den Griff bekommen kann.&lt;br /&gt;
&lt;br /&gt;
Hilfreich kann dies vor allem dann sein, wenn man z.B. über einen Kommandointerpreter (o.ä. Funktionen) direkt bestimmte EEPROM-Adressen manipulieren möchte. Auch wenn man über einen JTAG-Adapter (mk I oder mkII) den Programmablauf manipulieren möchte, indem man die EEPROM-Werte direkt ändert, kann diese Technik hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Im folgenden nun zwei Sourcelistings mit einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.h&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#inlcude &amp;lt;avr/eeprom.h&amp;gt;     // Die EEPROM-Definitionen/Macros der avr-libc einbinden&lt;br /&gt;
&lt;br /&gt;
#define   EESIZE   512      // Maximale Größe des EEPROMS&lt;br /&gt;
&lt;br /&gt;
#define   EE_DUMMY   0x000  // Dummyelement (Adresse 0 sollte nicht genutzt werden)&lt;br /&gt;
#define   EE_VALUE1  0x001  // Eine Bytevariable  &lt;br /&gt;
#define   EE_WORD1L  0x002  // Eine Wordvariable (Lowbyte)&lt;br /&gt;
#define   EE_WORD1H  0x003  // Eine Wordvariable (Highbyte)&lt;br /&gt;
#define   EE_VALUE2  0x004  // Eine weitere Bytevariable&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den Macros &#039;&#039;&#039;#define EE_VALUE1&#039;&#039;&#039; legt man den Namen und die Adresse der&lt;br /&gt;
&#039;Variablen&#039; fest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Die Adressen sollten fortlaufend, zumindest aber aufsteigend sortiert sein! Ansonsten besteht die Gefahr, daß man sehr schnell ein Durcheinander im EEPROM Speicher veranstaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Für den Compiler sind das lediglich Speicher-Adressen, über die auf das EEPROM zugegriffen wird. Der Compiler sieht nichts davon als eine echte Variable an und stößt sich daher auch nicht daran, wenn 2 Makros mit der gleichen Speicheradresse, bzw. überlappenden Speicherbereichen definiert werden. Es liegt einzig und alleine in der Hand des Programmierers, hier keinen Fehler zu machen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
   [EE_DUMMY]   = 0x00,&lt;br /&gt;
   [EE_VALUE1]  = 0x05,&lt;br /&gt;
   [EE_WORD1L]  = 0x01,   &lt;br /&gt;
   [EE_WORD1H]  = 0x00,&lt;br /&gt;
   [EE_VALUE2]  = 0xFF&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch die Verwendung eines Array, welches das gesammte EEPROM umfasst, bleibt&lt;br /&gt;
dem Compiler nicht anderes übrig, als das Array so zu platzieren, dass Element 0&lt;br /&gt;
des Arrays der Adresse 0 des EEPROMs entspricht. (&#039;&#039;Ich hoffe nur, dass die Compilerbauer daran nichts ändern!&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Wie man in dem obigen Codelisting auch sehen kann, hat das Verfahren einen kleinen Haken. Variablen die größer sind als 1 Byte, müssen etwas umständlicher&lt;br /&gt;
definiert werden. Benötigt man keine Initialisierung durch das Programm (was der Normalfall sein dürfte), dann kann man das auch so machen:&lt;br /&gt;
&lt;br /&gt;
Möchte man im EEPROM hintereinander beispielsweise Variablen, mit den Namen &#039;&#039;&#039;Wert&#039;&#039;&#039;, &#039;&#039;&#039;Anzahl&#039;&#039;&#039;, &#039;&#039;&#039;Name&#039;&#039;&#039; und &#039;&#039;&#039;Wertigkeit&#039;&#039;&#039; definieren, wobei Wert und Wertigkeit 1 Byte belegen sollen, Anzahl als 1 Wort (also 2 Bytes) und Name mit 10 Bytes reserviert werden soll, so geht auch folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes Makro definiert also seine Startadresse durch die Startadresse der unmittelbar vorhergehende &#039;Variablen&#039; plus der Anzahl der Bytes die von der vorhergehenden &#039;Variablen&#039; verbraucht werden. Dadurch ist man zumindest etwas auf der sicheren Seite, dass keine 2 &#039;Variablen&#039; im EEPROM überlappend definiert werden. Möchte man eine weitere &#039;Variable&#039; hinzufügen, so wird deren&lt;br /&gt;
Name, einfach anstelle der EE_LAST eingesetzt und eine neue Zeile für EE_LAST eingefügt, in der dann die Größe der &#039;Variablen&#039; festgelegt wird. Zb.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_PROZENT    ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( double ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EE_PROZENT legt die Startadresse für eine neue &#039;Variable&#039; des Datentyps double fest.&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf die EEPROM Werte kann dann z.B.so erfolgen:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t   temp1;&lt;br /&gt;
uint16_t  temp2;&lt;br /&gt;
&lt;br /&gt;
temp1 = eeprom_read_byte(EE_VALUE1);&lt;br /&gt;
temp2 = eeprom_read_word(EE_WORD1L);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ob die in der avr-libc vorhandenen Funktionen dafür verwendet werden können, weiß ich nicht. Aber in einigen Fällen muss man sich sowieso eigene Funktionen&lt;br /&gt;
bauen, welche die spezifischen Anforderungen (Interrupt - Atom Problem, etc.)&lt;br /&gt;
erfüllen.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist nur eine Möglichkeit, wie man dies realisieren kann. Sie bietet einem eine relativ einfache Art die EEPROM-Werte&lt;br /&gt;
auf beliebige Adressen zu legen oder Adressen zu ändern. Die Andere Möglichkeit besteht darin, die EEPROM-Werte wie folgt zu belegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
  0x00,                     //  ee_dummy&lt;br /&gt;
  0x05,                     //  ee_value1&lt;br /&gt;
  0x01,                     //  ee_word1L&lt;br /&gt;
  0x00,                     // (ee_word1H)&lt;br /&gt;
  0xFF                      //  ee_value2&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei kann man Variablen, die größer sind als 1 Byte einfacher definieren und&lt;br /&gt;
man muss nur die Highbyte- oder Lowbyte-Adresse in der &amp;quot;eeprom.h&amp;quot; definieren.&lt;br /&gt;
Allerdings muss man hier höllisch aufpassen, dass man nicht um eine oder mehrere&lt;br /&gt;
Positionen verrutscht!&lt;br /&gt;
&lt;br /&gt;
Welche der beiden Möglichkeiten man einsetzt, hängt vor allem davon ab, wieviele&lt;br /&gt;
Byte, Word und sonstige Variablen man benutzt. Gewöhnen sollte man sich an beide Varianten können ;)&lt;br /&gt;
&lt;br /&gt;
Kleine Schlussbemerkung:&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc unterstützt die Variante 1 und die Variante 2&lt;br /&gt;
* Der icc-avr Compiler unterstützt nur die Variante 2!&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Bei alten Versionen der avr-libc wurden nicht alle AVR Controller  untersützt. Z.B. bei der avr-libc Version 1.2.3 insbesondere bei AVRs &amp;quot;der neuen Generation&amp;quot; (ATmega48/88/168/169) funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register). In neueren Versionen (z.B. avr-libc 1.4.3 aus WinAVR 20050125) wurde die Zahl der unterstüzten Controller deutlich erweitert und eine Methode zur leichten Anpassung an zukünftige Controller eingeführt.&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Will oder kann man nicht auf die neue Version aktualisieren, kann der dort gezeigte Code auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;, gegebenfalls Schutz vor Unterbrechnung/Interrupt ergänzen &#039;&#039;uint8_t sreg; sreg=SREG; cli(); [EEPROM-Code] ; SREG=sreg; return;&#039;&#039;, siehe Abschnitt Interrupts). Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== EEPROM Register ===&lt;br /&gt;
Um das EEPROM anzusteuern sind drei Register von Bedeutung.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEAR Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL da in einem 8 Bit Register keine 512 Adressen adressiert werden können&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEDR Hier werden die Daten eingetragen die geschrieben werden sollen bzw. es enthält die gelesenen Daten&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EECR Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TABLE BORDER=1&amp;gt;&lt;br /&gt;
&amp;lt;TR&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;Bit&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;1&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;0&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&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;Name&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&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;TD&amp;gt;-&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERIE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEMWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERE&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;Read/Write&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;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&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;Init Value&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;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;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;TD&amp;gt;0&amp;lt;/TD&amp;gt;&lt;br /&gt;
&amp;lt;/TR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/TABLE&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bedeutung der Bits:&amp;lt;/U&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4-7 nicht belegt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 3 (EERIE) - EEPROM Ready Interrupt Enable:&amp;lt;/U&amp;gt; Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7) wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0 wird kein Interrupt ausgelöst.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 2(EEMWE) - EEPROM Master Write Enable:&amp;lt;/U&amp;gt; Dieses Bit bestimmt, daß wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE =0 ist und EEWE = 1 gesetzt wird hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst.&lt;br /&gt;
Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 1 (EEWE) - EEPROM Write Enable:&amp;lt;/U&amp;gt; Dieses Bit löst den Schreibvorgang aus wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist wird dieses Bit automatisch wieder auf 0 gesetzt und sofern EERIE gesetzt ist ein Interrupt ausgelöst. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ein Schreibvorgang sieht typischerweise wie folgt aus:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. EEPROM Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Daten übergeben an EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1 &amp;lt;BR&amp;gt;&lt;br /&gt;
5. (Optinal) Warten bis Schreibvorgang abgeschlossen ist.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 0 EERE – EEPROM Read Enable:&amp;lt;/U&amp;gt; Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit wenn das Bit EEWE=0 ist. ISt der LEsevorgang abgeschlossen wird das Bit wieder auf 0 gesetzt und das EEPROM ist für neue Lese/Schreibbefehle wieder bereit.&amp;lt;BR&amp;gt;&lt;br /&gt;
Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. Bereitschaft zum lesen prüfen (EEWE=0)&amp;lt;BR&amp;gt;&lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Lesezyklus auslösen mit EERE = 1&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Warten bis Lesevorgang abgeschlossen EERE = 0&amp;lt;BR&amp;gt;&lt;br /&gt;
5. Daten abholen aus EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bietet sich printf/sprintf an. &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t s[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( s, &amp;quot;Zählerstand: %d&amp;quot;, value );&lt;br /&gt;
    uart_puts( s );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, die Ausgabe stdout auf eigene Ausgabefunktionen umzuleiten. Dazu wird dem Ausgabemechanismus eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben (wie auch immer die Ausgabe stattfindet). Alle anderen, höheren, Funktionen greifen letztendlich auf diese eine Ausgabefunktion zurück, die die Ausgabe eines einzelnen Zeichens vornimmt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uart_init();&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
    printf( &amp;quot;Hello, world!\n&amp;quot; );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil aller printf-Varianten: Sie sind recht speicherintensiv.&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C- und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gnu-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Inline-Assembler: Die Assembleranweisungen werden direkt in den C-Code integriert. Eine Quellcode-Datei enthält somit C- und Assembleranweisungen&lt;br /&gt;
* Assembler-Dateien: Der Assemblercode befindet sich in eigenen Quellcodedateien. Diese werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung (NOP steht für No Operation). Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=Warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
   /* Verzögern der weiteren Programmausführung um&lt;br /&gt;
      genau 3 Taktzyklen */&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeintlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
&lt;br /&gt;
    /* leere Schleife - wird bei eingeschalteter Compiler-Optimierung   wegoptimiert */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* alternative Methode (keine Optimierung): */&lt;br /&gt;
    volatile uint16_t j;&lt;br /&gt;
    for (j = 0; j &amp;lt; 1000; j++)&lt;br /&gt;
      ;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützlicher &amp;quot;Assembler-Einzeiler&amp;quot; ist der Aufruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;);&#039;&#039;), da hierzu in älteren Versionen der avr-libc keine eigene Funktion existiert (in neueren Versionen &#039;&#039;sleep_cpu()&#039;&#039; aus sleep.h).&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise Delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil, dass die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static inline void delayloop16 (uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
    asm volatile (&amp;quot;cp  %A0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;cpc %B0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;breq 2f               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;1:                    \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;sbiw %0,1             \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;brne 1b               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;2:                    &amp;quot;  &lt;br /&gt;
                  : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
	          : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
    );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Anweisung wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen. Der Zeilenumbruch teilt dem Assembler mit, dass ein neuer Befehl beginnt.&lt;br /&gt;
* Als Sprung-Marken (Labels) werden Ziffern verwendet. Diese speziellen Labels sind mehrfach im Code verwendbar. Gesprungen wird jeweils zurück (b) oder vorwärts (f) zum nächsten ausffindbaren Label.&lt;br /&gt;
&lt;br /&gt;
Das Resultat zeigt ein Blick in die Assembler-Datei, die der Compiler mit der option &amp;lt;tt&amp;gt;-save-temps&amp;lt;/tt&amp;gt; nicht löscht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	cp  r24, __zero_reg__ 	 ;  count&lt;br /&gt;
	cpc r25, __zero_reg__ 	 ;  count&lt;br /&gt;
	breq 2f               &lt;br /&gt;
	1:                    &lt;br /&gt;
	sbiw r24,1             	 ;  count&lt;br /&gt;
	brne 1b               &lt;br /&gt;
	2:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc im Abschnitt [http://www.nongnu.org/avr-libc/user-manual/inline_asm.html Related Pages/Inline Asm]. &lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc Deutsche Einführung in Inline-Assembler]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
Wenn man mit dem AVR Studio arbeitet, kann alternativ auch das standardmäßig erstellte Makefile bearbeitet und folgende Zeilen eingefügt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
## Objects that must be built in order to link&lt;br /&gt;
OBJECTS = (alte Dateien...) useful.o&lt;br /&gt;
&lt;br /&gt;
## Compile&lt;br /&gt;
## Hier folgt eine Liste der gelinkten Dateien, darunter einfügen:&lt;br /&gt;
useful.o: ../useful.S&lt;br /&gt;
	$(CC) $(INCLUDES) $(ASMFLAGS) -c  $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Das war es schon. Allerdings gilt es zu beachten, dass das makefile über &amp;quot;Project -&amp;gt; Configuration options&amp;quot; selbst einzubinden ist, sonst wird es natürlich wieder überschrieben.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel eine Funktion &#039;&#039;superFunc&#039;&#039;, die alle Pins des Ports D auf &amp;quot;Ausgang&amp;quot; schaltet, eine Funktion &#039;&#039;ultraFunc&#039;&#039;, die die Ausgänge entsprechend des übergebenen Parameters schaltet, eine Funktion &#039;&#039;gigaFunc&#039;&#039;, die den Status von Port A zurückgibt und eine Funktion &#039;&#039;addFunc&#039;&#039;, die zwei Bytes zu einem 16-bit-Wort addiert. Die Zuweisungen im C-Code (PORTx = ...) verhindern, dass der Compiler die Aufrufe wegoptimiert und dienen nur zur Veranschaulichung der Parameterübergaben.&lt;br /&gt;
&lt;br /&gt;
Zuerst der Assembler-Code. Der Dateiname sei useful.S:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//; Arbeitsregister (ohne &amp;quot;r&amp;quot;) &lt;br /&gt;
workreg  = 16&lt;br /&gt;
workreg2 = 17&lt;br /&gt;
&lt;br /&gt;
//; Konstante:&lt;br /&gt;
ALLOUT = 0xff&lt;br /&gt;
&lt;br /&gt;
//; ** Setze alle Pins von PortD auf Ausgang **&lt;br /&gt;
//; keine Parameter, keine Rückgabe&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg  // beachte: _SFR_IO_ADDR()&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Setze PORTD auf übergebenen Wert **&lt;br /&gt;
//; Parameter in r24 (LSB immer bei &amp;quot;graden&amp;quot; Nummern)&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zustand von PINA zurückgeben **&lt;br /&gt;
//; Rückgabewerte in r24:r25 (LSB:MSB), hier nur LSB genutzt&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zwei Bytes addieren und 16-bit-Wort zurückgeben **&lt;br /&gt;
//; Parameter in r24 (Summand1) und r22 (Summand2) -&lt;br /&gt;
//;  Parameter sind Word-&amp;quot;aligned&amp;quot; d.h. LSB immer auf &amp;quot;graden&amp;quot;&lt;br /&gt;
//;  Registernummern. Bei 8-Bit und 16-Bit Paramtern somit &lt;br /&gt;
//;  beginnend bei r24 dann r22 dann r20 etc.&lt;br /&gt;
//; Rückgabewert in r24:r25&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   push workreg2&lt;br /&gt;
   clr workreg2&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
   pop workreg2&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
//; oh je - sorry - Mein AVR-Assembler ist eingerostet, hoffe das stimmt so...&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Makefile ist der Name der Assembler-Quellcodedatei einzutragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
ASRC = useful.S&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Aufruf erfolgt dann im C-Code so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
extern void superFunc(void);&lt;br /&gt;
extern void ultraFunc(uint8_t setVal);&lt;br /&gt;
extern uint8_t gigaFunc(void);&lt;br /&gt;
extern uint16_t addFunc(uint8_t w1, uint8_t w2);&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
[...]&lt;br /&gt;
  superFunc();&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
&lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
[...]&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird wieder in der lss-Datei ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
   superFunc();&lt;br /&gt;
 148:	0e 94 f6 00 	call	0x1ec&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
 14c:	85 e5       	ldi	r24, 0x55	; 85&lt;br /&gt;
 14e:	0e 94 fb 00 	call	0x1f6&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
 152:	0e 94 fd 00 	call	0x1fa&lt;br /&gt;
 156:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
  &lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
 158:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 15a:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 15c:	0e 94 ff 00 	call	0x1fe&lt;br /&gt;
 160:	8b bb       	out	0x1b, r24	; 27&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
 162:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 164:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 166:	0e 94 fc 00 	call	0x1f8&lt;br /&gt;
 16a:	89 2f       	mov	r24, r25&lt;br /&gt;
 16c:	99 27       	eor	r25, r25&lt;br /&gt;
 16e:	88 bb       	out	0x18, r24	; 24&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
000001ec &amp;lt;superFunc&amp;gt;:&lt;br /&gt;
// setze alle Pins von PortD auf Ausgang&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1ec:	0f 93       	push	r16&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
 1ee:	0f ef       	ldi	r16, 0xFF	; 255&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg&lt;br /&gt;
 1f0:	01 bb       	out	0x11, r16	; 17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 1f2:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 1f4:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001f6 &amp;lt;ultraFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// setze PORTD auf übergebenen Wert&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
 1f6:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
   ret&lt;br /&gt;
 1f8:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fa &amp;lt;gigaFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Zustand von PINA zurückgeben&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
 1fa:	89 b3       	in	r24, 0x19	; 25&lt;br /&gt;
   ret&lt;br /&gt;
 1fc:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fe &amp;lt;addFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// zwei Bytes addieren und 16-bit-Wort zurückgeben&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1fe:	0f 93       	push	r16&lt;br /&gt;
   push workreg2&lt;br /&gt;
 200:	1f 93       	push	r17&lt;br /&gt;
   clr workreg2&lt;br /&gt;
 202:	11 27       	eor	r17, r17&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
 204:	06 2f       	mov	r16, r22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
 206:	08 0f       	add	r16, r24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
 208:	11 1d       	adc	r17, r1&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
 20a:	c8 01       	movw	r24, r16&lt;br /&gt;
   pop workreg2&lt;br /&gt;
 20c:	1f 91       	pop	r17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 20e:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 210:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zuweisung von Registern zu Parameternummer und die Register für die Rückgabewerte sind in den &amp;quot;Register Usage Guidelines&amp;quot; der avr-libc-Dokumentation erläutert.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/assembler.html avr-libc-Dokumentation: Related Pages/avr-libc and assembler programs]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage avr-libc-Dokumentation: Related Pages/FAQ/&amp;quot;What registers are used by the C compiler?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
== Globale Variablen für Datenaustausch ==&lt;br /&gt;
&lt;br /&gt;
Oftmals kommt man um globale Variablen nicht herum, z.B. um den Datenaustausch zwischen Hauptprogramm und Interrupt-Routinen zu realisieren. &lt;br /&gt;
Hierzu muss man im Assembler wissen, wo genau die Variable vom C-Compiler abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
Hierzu muss die Variable, hier &amp;quot;zaehler&amp;quot; genannt, zuerst im C-Code als Global definiert werden, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
int16_t main (void)&lt;br /&gt;
{&lt;br /&gt;
    // irgendein Code, in dem zaehler benutzt werden kann&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im foldenden Assembler-Beispiel wird der Externe Interrupt0  verwendet, um den Zähler hochzuzählen. Es fehlen die Initialisierungen des Interrupts und die Interrupt-Freigabe, so richtig sinnvoll ist der Code auch nicht, aber er zeigt (hoffentlich) wie es geht.&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit Interrupt-Vektoren gilt beim GCC-Assembler das Gleiche, wie bei C: Man muss die exakte Schreibweise beachten, ansonsten wird nicht der Interrupt-Vektor angelegt, sondern eine neue Funktion - und man wundert sich, dass nichts funktionert (vgl. das AVR-GCC-Handbuch).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
.extern zaehler&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      //; wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     //; Status-Register (SREG) sichern!&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     lds temp,zaehler               //; Wert aus dem Speicher lesen&lt;br /&gt;
     inc temp                       //; bearbeiten&lt;br /&gt;
     sts zaehler,temp               //; und wieder zurückschreiben&lt;br /&gt;
&lt;br /&gt;
     pop temp                       //; die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen im Assemblerfile anlegen ===&lt;br /&gt;
&lt;br /&gt;
Alternativ können Variablen aber auch im Assemblerfile angelegt werden. Dadurch kann auf eine .c-Datei verzichtet werden. Für das obige Beispiel könnte der Quelltext dann die Dateien zaehl_asm.S und zaehl_asm.h abgelegt werden, so dass nur noch zaehl_asm.S mit kompiliert werden müsste.&lt;br /&gt;
&lt;br /&gt;
Anstatt im Assemblerfile über das Schlüsselwort &#039;&#039;.extern &#039;&#039; auf eine vorhandene Variable zu verweisen, wird dazu mit dem Schlüsselwort &#039;&#039;.comm&#039;&#039; die benötigte Anzahl von Bytes für eine Variable reserviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.S&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
//; 1 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 1&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Headerdatei wird dann auf die Variable nur noch verwiesen (Schlüsselwort &#039;&#039;extern&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef ZAEHL_ASM_H&lt;br /&gt;
#define ZAEHL_ASM_H&lt;br /&gt;
&lt;br /&gt;
extern volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu globalen Variablen in C werden so angelegte Variablen nicht automatisch mit dem Wert 0 initialisiert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer als 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Variablen, die größer als &#039;&#039;&#039;ein&#039;&#039;&#039; Byte sind, können in Assembler auf ähnliche Art angesprochen werden. Hierzu müssen nur genug Bytes angefordert werden, um die Variable aufzunehmen. Soll z.B. für den Zähler eine Variable vom Typ &#039;&#039;unsigned long&#039;&#039;, also &#039;&#039;uint32_t&#039;&#039; verwendet werden, so müssen 4 Bytes reserviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die dazugehörige Deklaration im Headerfile wäre dann:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
extern volatile uint32_t zaehler;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen, die größer als ein Byte sind, werden die Werte beginnend mit dem niederwertigsten Byte im RAM abgelegt. Das folgende Codeschnippsel zeigt, wie unter Assembler auf die einzelnen Bytes zugegriffen werden kann. Dazu wird im Interrupt nun ein 32-Bit Zähler erhöht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      // wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     // Status-Register (SREG) sichern !&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     // 32-Bit-Zähler incrementieren&lt;br /&gt;
     lds temp, (zaehler + 0)        // 0. Byte (niederwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 0), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
     lds temp, (zaehler + 1)        // 1. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 1), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 2)        // 2. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 2), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 3)        // 3. Byte (höchstwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 3), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
RAUS:&lt;br /&gt;
     pop temp                       // die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TODO:&#039;&#039;&#039; 16-Bit / 32-Bit Variablen, Zugriff auf Arrays (Strings)&lt;br /&gt;
&lt;br /&gt;
= Anhang =&lt;br /&gt;
&lt;br /&gt;
== Besonderheiten bei der Anpassung bestehenden Quellcodes ==&lt;br /&gt;
&lt;br /&gt;
Einige Funktionen, die in frühren Versionen der avr-libc vorhanden waren, werden inzwischen als veraltet angesehen. Sie sind nicht mehr vorhanden oder als &#039;&#039;deprecated&#039;&#039; (missbilligt) ausgewiesen und Definitionen in &amp;lt;compat/deprecated.h&amp;gt; verschoben. Es empfiehlt sich, vorhandenen Code zu portieren und die alten Funktionen nicht mehr zu nutzen, auch wenn diese noch zur Verfügung stehen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zur Deklaration von Interrupt-Routinen ===&lt;br /&gt;
&lt;br /&gt;
Die Funktionen (eigentlich Makros) &#039;&#039;SIGNAL&#039;&#039; und &#039;&#039;INTERRUPT&#039;&#039; zur Deklaration von Interruptroutinen sollten nicht mehr genutzt werden. &lt;br /&gt;
&lt;br /&gt;
In aktuellen Versionen der avr-libc (z.B. avr-libc 1.4.3 aus WinAVR 20060125) werden Interruptroutinen, die &#039;&#039;&#039;nicht&#039;&#039;&#039; durch andere Interrupts &#039;&#039;&#039;unterbrechbar&#039;&#039;&#039; sind, mit ISR deklariert (siehe Abschnitt im Hauptteil). Auch die Benennung wurden vereinheitlicht und an die üblichen Bezeichnungen in den AVR Datenblättern angepasst. In der Dokumentation der avr-libc sind alte und neue Bezeichnungen in der Tabelle gegenübergestellt. Die erforderlichen Schritte zur Portierung:&lt;br /&gt;
&lt;br /&gt;
* #include von avr/signal.h entfernen&lt;br /&gt;
* SIGNAL duch ISR ersetzen&lt;br /&gt;
* Name des Interrupt-Vektors anpassen (SIG_* durch entsprechendes *_vect)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für die Anpassung zuerst ein &amp;quot;alter&amp;quot; Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer2 Output Compare bei einem ATmega8 */&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE2)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt wird der Vektor mit TIMER2 COMP bezeichnet. Die Bezeichnung in der avr-libc entspricht dem Namen im Datenblatt, Leerzeichen werden durch Unterstriche (_) ersetzt und ein _vect angehängt. &lt;br /&gt;
&lt;br /&gt;
Der neue Code sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt; &lt;br /&gt;
/* signal.h entfällt */&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER2_COMP_vect)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Unklarheiten bezüglich der neuen Vektorlabels hilft (noch) ein Blick in die Headerdatei des entsprechenden Controllers. Für das vorherige Beispiel also der Blick in die Datei iom8.h für den ATmega8, dort findet man die veraltete Bezeichnung unterhalb der aktuellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect		_VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2		_VECTOR(3)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Nachfolgendes mit avr-libc 1.4.5 (in WinAVR 1/2007 behoben - noch eine Weile auskommentiert lassen und dann löschen.&lt;br /&gt;
Konnte in alten Versionen signal.h ohne interrupt.h eingebunden werden, erhält man bei Verwendung der avr-libc Version 1.4.3 (WinAVR 2/2005) beim Compilieren eine Fehlermeldung, da mit signal.h nicht die erforderlichen Definitionen eingebunden werden. Der Lösungsvorschlag in signal.h auch interrupt.h einzubinden, wurde von den avr-libc-Enwicklern akzeptiert und das Problem ist  im Quellcode (CVS) bereits behoben. Es ist aber noch keine avr-libc-&amp;quot;Release&amp;quot; bzw. noch kein WinAVR mit dieser avr-libc-Korrektur verfügbar (Stand 5.2.2006). Will oder kann man den Quellcode nicht aktualisieren, gibt es folgende Alternativen:&lt;br /&gt;
* in Quellcodedateien, in denen nur avr/signal.h eingebunden wird, interrupt.h einbinden (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;). signal.h weiterhin einbinden, falls Kompatibiltät mit alten Versionen gewünscht ist.&lt;br /&gt;
* in der Datei signal.h (bein WinAVR in c:/WinAVR/avr/include/avr/signal.h) ein (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;) ergänzen --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;unterbrechbare&#039;&#039;&#039; Interruptroutinen, die mittels &#039;&#039;INTERRUPT&#039;&#039; deklariert sind, gibt es keinen direkten Ersatz in Form eines Makros. Solche Routinen sind laut Dokumentation der avr-libc in folgender Form zu deklarieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* ** alt ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
INTERRUPT(SIG_OVERFLOW0)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ** neu: ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void TIMER0_OVF_vect(void) __attribute__((interrupt));&lt;br /&gt;
void TIMER0_OVF_vect(void) &lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von &#039;&#039;INTERRUPT&#039;&#039; die Header-Datei &#039;&#039;compat/deprecated.h&#039;&#039; einzubinden. Man sollte bei dieser Gelegenheit jedoch nochmals überprüfen, ob die Funktionalität von &#039;&#039;INTERRUPT&#039;&#039; tatsächlich gewollt ist. In vielen Fällen wurde &#039;&#039;INTERRUPT&#039;&#039; dort genutzt, wo eigentlich &#039;&#039;SIGNAL&#039;&#039; (nunmehr &#039;&#039;ISR&#039;&#039;) hätte genutzt werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;inp&#039;&#039; und &#039;&#039;outp&#039;&#039; zum Einlesen bzw. Schreiben von Registern sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
unsigned char i, j;&lt;br /&gt;
&lt;br /&gt;
// alt:&lt;br /&gt;
  i = inp(PINA);&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  outp(PORTB, j);&lt;br /&gt;
&lt;br /&gt;
// neu (nicht mehr wirklich neu...):&lt;br /&gt;
  i = PINA&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  PORTB = j;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von inp und outp die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Zugriff auf Bits in Registern ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;cbi&#039;&#039; und &#039;&#039;sbi&#039;&#039; zum Löschen und Setzen von Bits sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg. Die Bezeichnung ist ohnehin irreführend da die Funktionen nur für Register mit Adressen im unteren Speicherbereich tatsächlich in die Assembleranweisungen cbi und sbi übersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// alt:&lt;br /&gt;
  sbi(PORTB, PB2);&lt;br /&gt;
  cbi(PORTC, PC1);&lt;br /&gt;
&lt;br /&gt;
// neu (auch nicht mehr wirklich neu...):&lt;br /&gt;
  PORTB |=  (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
  PORTC &amp;amp;= ~(1&amp;lt;&amp;lt;PC1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von sbi und cbi die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden. Wer unbedingt will, kann sich natürlich eigene Makros mit aussagekräftigeren Namen definieren. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &amp;amp;= ~(1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Selbstdefinierte (nicht-standardisierte) ganzzahlige Datentypen ===&lt;br /&gt;
&lt;br /&gt;
Bei den im Folgenden genannten Typdefinitionen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich, bei der Überarbeitung von altem Code die im Abschnitt &#039;&#039;standardisierten ganzzahligen Datentypen&#039;&#039; beschriebenen Datentypen zu nutzen (stdint.h) und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef unsigned char      BYTE;       // besser: uint8_t  aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned short     WORD;       // besser: uint16_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long      DWORD;      // besser: uint32_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long long QWORD;      // besser: uint64_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; BYTE : Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 255. &lt;br /&gt;
&lt;br /&gt;
; WORD : Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 65535. &lt;br /&gt;
&lt;br /&gt;
; DWORD : Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
; QWORD : Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Funktionen im Makefile ==&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken (Libraries/.a-Dateien) hinzufügen ===&lt;br /&gt;
&lt;br /&gt;
Um Funktionen aus Bibliotheken (&amp;quot;echte&amp;quot; Libraries, *.a-Dateien) zu nutzen, sind dem Linker die Namen der Bibliotheken als Parameter zu übergeben. Dazu ist die Option -l (kleines L) vorgesehen, an die der Name der Library angehängt wird. &lt;br /&gt;
&lt;br /&gt;
Dabei ist zu beachten, dass der Name der Library und der Dateiname der Library nicht identisch sind. Der hinter -l angegebene Name entspricht dem Dateinamen der Library ohne die Zeichenfolge &#039;&#039;lib&#039;&#039; am Anfang des Dateinamens und ohne die Endung &#039;&#039;.a&#039;&#039;. Sollen z.B. Funktionen aus einer Library mit dem Dateinamen &#039;&#039;libefsl.a&#039;&#039; eingebunden (gelinkt) werden, lautet der entsprechende Parameter -lefsl (vergl. auch -lm zum Anbinden von libm.a). &lt;br /&gt;
&lt;br /&gt;
In Makefiles wird traditonell eine make-Variable LDLIBS genutzt, in die &amp;quot;l-Parameter&amp;quot; abgelegt werden. Die WinAVR-makefile-Vorlage enthält diese Variable zwar nicht, dies stellt jedoch keine Einschränkung dar, da alle in der make-Variable LDFLAGS abgelegten Parameter an den Linker weitergereicht werden. &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Einbinden von Funktionen aus einer Library efsl (Dateiname libefsl.a)&lt;br /&gt;
LDFLAGS += -lefsl&lt;br /&gt;
# Einbinden von Funktionen aus einer Library xyz (Dateiname libxyz.a)&lt;br /&gt;
LDFLAGS += -lxyz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Liegen die Library-Dateien nicht im Standard Library-Suchpfad, sind die Pfade mittels Parameter &#039;&#039;-L&#039;&#039; ebenfalls anzugeben. (Der vordefinierte Suchpfad kann mittels &#039;&#039;avr-gcc --print-search-dirs&#039;&#039; angezeigt werden.)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Projekt (&amp;quot;superapp2&amp;quot;), in dem der Quellcode von zwei Libraries (efsl und xyz) und der Quellcode der eigentlichen Anwendung in verschiedenen Verzeichnissen mit der folgenden &amp;quot;Baumstruktur&amp;quot; abgelegt sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
superapp2&lt;br /&gt;
|&lt;br /&gt;
+----- efslsource (darin libefsl.a)&lt;br /&gt;
|&lt;br /&gt;
+----- xyzsource (darin libxyz.a)&lt;br /&gt;
|&lt;br /&gt;
+----- firmware (darin Anwendungs-Quellcode und Makefile)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt, dass im Makefile die Verzeichnis efslsource und xyzsource in den Library-Suchpfad aufzunehmen sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
LDFLAGS += -L../efslsource/ -L../xyzsource/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fuse-Bits ===&lt;br /&gt;
&lt;br /&gt;
Zur Berechnung der Fuse-Bits bietet sich neben dem Studium des Datenblattes auch der [http://palmavr.sourceforge.net/cgi-bin/fc.cgi AVR Fuse Calculator] an. Gewarnt werden muss vor der Benutzung von PonyProg, weil dort durch die negierte Darstellung gern Fehler gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Soll die Programmierung von Fuse- und Lockbits automatisiert werden, kann man dies ebenfalls durch Einträge im Makefile vornehmen, die beim Aufruf von &amp;quot;make program&amp;quot; an die genutzte Programmiersoftware übergeben werden. In der makefile-Vorlage von WinAVR (und mfile) gibt es dafuer jedoch keine &amp;quot;Ausfüllhilfe&amp;quot; (Stand 9/2006). Die folgenden Ausführungen gelten für die Programmiersoftware [[AVRDUDE]] (Standard in der WinAVR-Vorlage), können jedoch sinngemäß auf andere Programmiersoftware übertragen werden, die die Angabe der Fuse- und Lockbits-Einstellungen per Kommandozeilenparameter unterstützt (z.B. stk500.exe). Im einfachsten Fall ergänzt man im Makefile einige Variablen, deren Werte natürlich vom verwendeten Controller und den gewünschten Einstellungen abhängen (vgl. Datenblatt Fuse-/Lockbits):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#---------------- Programming Options (avrdude) ----------------&lt;br /&gt;
&lt;br /&gt;
#...&lt;br /&gt;
#Beispiel! f. ATmega16 - nicht einfach uebernehmen! Zahlenwerte anhand&lt;br /&gt;
#--------- des Datenblatts nachvollziehen und gegebenenfalls aendern.&lt;br /&gt;
#&lt;br /&gt;
AVRDUDE_WRITE_LFUSE = -U lfuse:w:0xff:m&lt;br /&gt;
AVRDUDE_WRITE_HFUSE = -U hfuse:w:0xd8:m&lt;br /&gt;
AVRDUDE_WRITE_LOCK  = -U lock:w:0x2f:m&lt;br /&gt;
#...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit diese Variablen auch genutzt werden, ist der Aufruf von avrdude im Makefile entsprechend zu ergänzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Program the device.  &lt;br /&gt;
program: $(TARGET).hex $(TARGET).eep&lt;br /&gt;
# ohne Fuse-/Lock-Einstellungen (nach WinAVR Vorlage Stand 4/2006)&lt;br /&gt;
#	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
#        $(AVRDUDE_WRITE_EEPROM)&lt;br /&gt;
# mit Fuse-/Lock-Einstellungen&lt;br /&gt;
        $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_LFUSE) \&lt;br /&gt;
        $(AVRDUDE_WRITE_HFUSE) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
        $(AVRDUDE_WRITE_EEPROM) $(AVRDUDE_WRITE_LOCK)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weiter Möglichkeit besteht darin, die Fuse- und Lockbit-Einstellungen vom Preprozessor/Compiler generieren zu lassen. Die Fuse-Bits werden dann bei Verwendung von AVRDUDE in eigene Hex-Files geschrieben. Hierzu kann man z.B. folgendes Konstrukt verwenden:&lt;br /&gt;
&lt;br /&gt;
In eine der C-Sourcen wird eine Variable je Fuse-Byte vom Typ &#039;&#039;unsigned char&#039;&#039; deklariert und in eine extra Section gepackt. Dies kann entweder in einem vorhandenen File passieren oder in ein neues (z.B. fuses.c) geschrieben werden. Das File muss im Makefile aber auf jeden Fall mit kompiliert und gelinkt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// tiny 2313 fuses low byte&lt;br /&gt;
#define CKDIV8  7&lt;br /&gt;
#define CKOUT   6&lt;br /&gt;
#define SUT1    5&lt;br /&gt;
#define SUT0    4&lt;br /&gt;
#define CKSEL3  3&lt;br /&gt;
#define CKSEL2  2&lt;br /&gt;
#define CKSEL1  1&lt;br /&gt;
#define CKSEL0  0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses high byte&lt;br /&gt;
#define DWEN       7&lt;br /&gt;
#define EESAVE     6&lt;br /&gt;
#define SPIEN      5&lt;br /&gt;
#define WDTON      4&lt;br /&gt;
#define BODLEVEL2  3&lt;br /&gt;
#define BODLEVEL1  2&lt;br /&gt;
#define BODLEVEL0  1&lt;br /&gt;
#define RSTDISBL   0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses extended byte&lt;br /&gt;
#define SELFPRGEN  0&lt;br /&gt;
&lt;br /&gt;
#define LFUSE         __attribute__ ((section (&amp;quot;lfuses&amp;quot;)))&lt;br /&gt;
#define HFUSE         __attribute__ ((section (&amp;quot;hfuses&amp;quot;)))&lt;br /&gt;
#define EFUSE         __attribute__ ((section (&amp;quot;efuses&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// select ext crystal 3-8Mhz&lt;br /&gt;
unsigned char lfuse LFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;CKDIV8) | (1&amp;lt;&amp;lt;CKOUT) | (1&amp;lt;&amp;lt;CKSEL3) | (1&amp;lt;&amp;lt;CKSEL2) | &lt;br /&gt;
      (0&amp;lt;&amp;lt;CKSEL1) | (1&amp;lt;&amp;lt;CKSEL0) | (0&amp;lt;&amp;lt;SUT1) | (1&amp;lt;&amp;lt;SUT0) );&lt;br /&gt;
unsigned char hfuse HFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;DWEN) | (1&amp;lt;&amp;lt;EESAVE) | (0&amp;lt;&amp;lt;SPIEN) | (1&amp;lt;&amp;lt;WDTON) | &lt;br /&gt;
      (1&amp;lt;&amp;lt;BODLEVEL2) | (1&amp;lt;&amp;lt;BODLEVEL1) | (0&amp;lt;&amp;lt;BODLEVEL0) | (1&amp;lt;&amp;lt;RSTDISBL) );&lt;br /&gt;
unsigned char efuse EFUSE =&lt;br /&gt;
    ((0&amp;lt;&amp;lt;SELFPRGEN));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG: Die Bitpositionen wurden nicht vollständig getestet!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Eine &amp;quot;1&amp;quot; bedeutet hier, dass das Fuse-Bit &#039;&#039;nicht&#039;&#039; programmiert wird - die Funktion also i.A. nicht aktiviert ist. Eine &amp;quot;0&amp;quot; hingegen aktiviert die meisten Funktionen. Dies ist wie im Datenblatt (1 = unprogrammed, 0 = programmed).&lt;br /&gt;
&lt;br /&gt;
Das Makefile muss nun noch um folgende Targets erweitert werden (mit Tabulator einrücken - nicht mit Leerzeichen):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
lfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j lfuses --change-section-address lfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-lfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-lfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U lfuse:w:$(TARGET)-lfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
hfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j hfuses --change-section-address hfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-hfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-hfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U hfuse:w:$(TARGET)-hfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
efuses: build&lt;br /&gt;
        -$(OBJCOPY) -j efuses --change-section-address efuses=0 \&lt;br /&gt;
         -O ihex $(TARGET).elf $(TARGET)-efuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-efuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U efuse:w:$(TARGET)-efuse.hex;&lt;br /&gt;
        fi;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Target &amp;quot;clean&amp;quot; muss noch um die Zeilen&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
        $(REMOVE) $(TARGET)-lfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-hfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-efuse.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
erweitert werden, wenn auch die Fuse-Dateien gelöscht werden sollen.&lt;br /&gt;
&lt;br /&gt;
Um nun die Fusebits des angeschlossenen Controllers zu programmieren muss lediglichein &amp;quot;make lfuses&amp;quot;, &amp;quot;make hfuses&amp;quot; oder &amp;quot;make efuses&amp;quot; gestartet werden.&lt;br /&gt;
Bei den Fuse-Bits ist besondere Vorsicht geboten, da diese das Programmieren des Controllers unmöglich machen können. Also erst programmieren, wenn man einen HV-Programmierer hat oder ein paar Reserve-AVRs zur Hand ;-)&lt;br /&gt;
&lt;br /&gt;
Um weiterhin den &amp;quot;normalen&amp;quot; Flash beschreiben zu können, ist es wichtig, für das Target &amp;quot;*.hex&amp;quot; im Makefile nicht nur &amp;quot;-R .eeprom&amp;quot; als Parameter zu übergeben sondern zusätzlich noch &amp;quot;-R lfuses -R efuses -R hfuses&amp;quot;. Sonst bekommt AVRDUDE Probleme diese Sections in den Flash (wo sie ja nicht hingehören) zu schreiben.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[AVR_Fuses#Vergleich_der_Fuses_bei_verschiedenen_Programmen|Vergleich der Fuses bei verschiedenen Programmen]]&lt;br /&gt;
&lt;br /&gt;
== Externe Referenzspannung des internen Analog-Digital-Wandlers ==&lt;br /&gt;
&lt;br /&gt;
Die minimale (externe) Referenzspannung des ADC darf nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. z.B. beim ATMEGA8 darf sie laut Datenblatt (S.245, Tabelle 103, Zeile &amp;quot;VREF&amp;quot;) 2,0V nicht unterschreiten. HINWEIS: diese Information findet sich erst in der letzten Revision (Rev. 2486O-10/04) des Datenblatts.&lt;br /&gt;
&lt;br /&gt;
Meiner &amp;lt;!-- Wer? - es gibt inzwischen x Leute die mehr oder weniger viel in diesem Artikel geschrieben haben --&amp;gt; eigenen Erfahrung nach kann man aber (auf eigene Gefahr und natürlich nicht für Seriengeräte) durchaus noch ein klein wenig weiter heruntergehen, bei dem von mir unter die Lupe genommenen ATMEGA8L (also die Low-Voltage-Variante) funktioniert der ADC bei 5V Betriebsspannung mit bis zu VREF=1,15V hinunter korrekt, ab 1,1V und darunter digitalisiert er jedoch nur noch Blödsinn). Ich würde sicherheitshalber nicht unter 1,5V gehen und bei niedrigeren Betriebsspannungen mag sich die Untergrenze für VREF am Pin AREF ggf. nach oben&#039;&#039;&#039;(!)&#039;&#039;&#039; verschieben.&lt;br /&gt;
&lt;br /&gt;
In der letzten Revision des Datenblatts ist außerdem korrigiert, dass ADC4 und ADC5 sehr wohl 10 Bit Genauigkeit bieten (und nicht bloß 8 Bit, wie in älteren Revisionen irrtümlich angegeben.)&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* stdio.h, malloc() &lt;br /&gt;
* Code-Optimierungen (&amp;quot;tricks&amp;quot;), siehe auch Application Note [http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf AVR035: Efficient C Coding for AVR]&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
* SPI siehe [http://www.uni-koblenz.de/~physik/informatik/MCU/SPI.pdf SPI Bus mit Atmel AVR]&lt;br /&gt;
* I²C / TWI Bus [http://www.roboternetz.de/wissen/index.php/TWI]&lt;br /&gt;
* Bootloader (bez. auf boot.h)&lt;br /&gt;
* CAN-Bus&lt;br /&gt;
* Einsatz von einfachen Betriebssystemen auf dem AVR&lt;br /&gt;
* Übersicht zu den C bzw. GCC-predefined Makros (__DATE__, __TIME__,...)&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
* ADC ; &lt;br /&gt;
* Timer&lt;br /&gt;
* USB ; Steuerung mit USB&lt;br /&gt;
* Multiplexen Siebensegment&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28249</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28249"/>
		<updated>2008-05-27T16:14:08Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Was tun, wenn&amp;#039;s nicht &amp;quot;klappt&amp;quot;? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[AVR-GCC]] erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Progammiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong]. Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern, weder in Assembler noch in einer anderen Sprache. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR Controllern finden. Bei WinAVR gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden stetig weiterentwickelt. Erläuterungen und Beispiele beziehen sich auf den C-Compiler avr-gcc ab Version 3.4 und die avr-libc ab Version 1.4.3. Die Unterschiede zu älteren Versionen werden im Haupttext und Anhang zwar erläutert, Anfängern sei jedoch empfohlen, die aktuellen Versionen zu nutzen (für MS-Windows: aktuelle Version des [[WinAVR]]-Pakets). &lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in PDF-Form erhältlich bei:&lt;br /&gt;
http://www.siwawi.arubi.uni-kl.de/avr_projects/AVR-GCC-Tutorial_-_www_mikrocontroller_net.pdf&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels avr-gcc/avr-libc zu erstellen und zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR Controllers, der vom avr-gcc Compiler unterstützt wird (alle ATmegas und die meisten AT90, siehe Dokumentation der avr-libc für unterstützte Typen). Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man [[AVR#Starterkits|hier]].&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc Compiler und die avr-libc. Kostenlos erhältlich für nahezu alle Plattformen und Betriebssysteme. Für MS-Windows im Paket [[WinAVR]]; für Unix/Linux siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]]).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder nur die eigenen C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
&lt;br /&gt;
* Die [[AVR Checkliste]] durcharbeiten.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/2 GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  auf den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
&lt;br /&gt;
* Die Beispieleprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweisen sind aber auch auf C-Programme übertragbar.&lt;br /&gt;
&lt;br /&gt;
* Einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien, um das Problem nachzuvollziehen, sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z. B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu zwei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer Integrierten Entwicklungsumgebung (IDE), bei der alle Einstellungen z.B. in Dialogboxen durchgeführt werden können. Unter Anderen kann AVRStudio ab Version 4.12 (kostenlos bei atmel.com) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs für den avr-gcc (ohne Anspruch auf Vollständigkeit): AtmanAvr C (relativ günstig), KamAVR (kostenlos) VMLab (ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand der universellen und plattformunabhängigen Vorgehensweise mittels make und Makefiles näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind lediglich Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xff;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5a)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/;  // (5b)&lt;br /&gt;
   }                         // (5c)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (6)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In der mit (1) markierten Zeile, wird eine sogenannte Header-Datei eingebunden. In io.h sind die Registernamen definiert, die im späteren Verlauf genutzt werden.&lt;br /&gt;
&lt;br /&gt;
* Bei (2) beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion main.&lt;br /&gt;
&lt;br /&gt;
* Die Anschlüsse eines AVR (&amp;quot;Beinchen&amp;quot;) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Richtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B zu Ausgängen.&lt;br /&gt;
&lt;br /&gt;
* (4) stellt die Werte der Ausgänge ein. Die den ersten beiden  Bits des Ports zugeordnete Anschluss (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht-aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential).&lt;br /&gt;
&lt;br /&gt;
* (5) ist die so genannte Hauptschleife (main-loop). Dies ist eine Programmschleife, welche kontinuierlich wiederkehrende Befehle enthält. In diesem Beispiel ist sie leer. Der Controller durchläuft die Schleife immer wieder, ohne das etwas passiert (außer das Strom verbraucht wird). Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Wäre die Schleife nicht vorhanden, würde der Zustand des Controllers nach dem Programmende undefiniert.&lt;br /&gt;
&lt;br /&gt;
* (6) wäre das Programmende. Die Zeile ist nur aus Gründen der C- Kompatibilität enthalten: int main(void) besagt, dass die Funktion einen Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein auf den Controller lauffähiges Programm zu übersetzen,  wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen Makefile (ohne Endung) im selben Verzeichnis in dem auch die Datei main.c mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im folgenden Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\tmp\gcc_tut\quickstart&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei main.hex, in der der Code für den AVR enthalten ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.B. über In-System-Programming (ISP) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann (z.B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio), kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine LED leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode an einen dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Exkurs: Makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Plattformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.com) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das Makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den im Makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im Makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten Makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt [[AVR-GCC-Tutorial#EEPROM|Speicherzugriffe]]), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendeten Controllers gesetzt. Eine Liste der von avr-gcc und der avr-libc unterstützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien eintragen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
# nicht auskommentiert: EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Das erstellte Makefile und der Code müssen im gleichen Ordner sein, auch sollte der Dateiname nicht verändert werden. &lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem Makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschinencode erzeugt. Beim Update auf eine neue Compilerversion ist zu beachten, dass diese möglicherweise intern andere Optimierungsalgorithmen verwenden und sich dadurch die Größe des Machinencodes etwas ändert, ohne dass man im Quellcode etwas geändert hat.&lt;br /&gt;
&lt;br /&gt;
Als Orientierungswerte die Größe des Maschinencodes bei verschiedenen Optionen für einen nicht näher spezifizierten relativ kleinen Testcode bei Verwendung einer nicht näher spezifizierten Compilerversion. &lt;br /&gt;
&lt;br /&gt;
* -O0 : 12&#039;217 Byte&lt;br /&gt;
* -O1 : 9&#039;128 Byte&lt;br /&gt;
* -O2 : 1&#039;670 Byte&lt;br /&gt;
* -O3 : 3&#039;004 Byte&lt;br /&gt;
* -Os : 1&#039;695 Byte&lt;br /&gt;
&lt;br /&gt;
Im diesem Testfall führt die Option -O2 mit zum kompaktesten Code, dies  allerdings hier nur mit 25 Bytes &amp;quot;Vorsprung&amp;quot;. Es kann durchaus sein, dass nur wenige Programmerweiterungen dazu führen, dass Compilieren mit -Os wieder in kompakteren Code resultiert.&lt;br /&gt;
&lt;br /&gt;
siehe dazu auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/using_tools.html#gcc_optO avr-libc manual Abschnitt Using the gnu-tools/Compiler-Optionen]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_optflags avr-libc Manual FAQ Nr. 16] (Stand avr-libc Version 1.4.5)&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wird hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (großes S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Taktfrequenz ===&lt;br /&gt;
&lt;br /&gt;
Neuere Versionen der WinAVR/Mfile Vorlage für Makefiles beinhalten die Definition einer Variablen F_CPU (WinAVR 2/2005). Darin wird die Taktfrequenz des Controllers in Hertz eingetragen. Die Definition steht dann im gesamten Projekt ebenfalls unter der Bezeichnung F_CPU zur Verfügung (z.B. um daraus  UART-, SPI- oder ADC-Frequenzeinstellungen abzuleiten).&lt;br /&gt;
&lt;br /&gt;
Die Angabe hat rein &amp;quot;informativen&amp;quot; Charakter, die tatsächliche Taktrate wird über den externen Takt (z.B. Quarz) bzw. die Einstellung des internen R/C-Oszillators  bestimmt. Die Nutzung von F_CPU hat also nur Sinn, wenn die Angabe mit dem tatsächlichen Takt übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Innerhalb neuerer Versionen der avr-libc (ab Version 1.2) wird die Definition der Taktfrequenz (F_CPU) zur Berechnung der Wartefunktionen in delay.h genutzt. Diese funktionieren nur dann korrekt, wenn F_CPU mit der tatsächlichen Taktfrequenz übereinstimmt.&lt;br /&gt;
F_CPU muss dazu jedoch nicht unbedingt im makefile definiert werden. Es reicht aus, wird aber bei mehrfacher Anwendung unübersichtlich, vor &#039;&#039;#include &amp;lt;util/delay.h&amp;gt;&#039;&#039; (veraltet: &#039;&#039;#include &amp;lt;avr/delay.h&amp;gt;&#039;&#039;) ein &#039;&#039;#define F_CPU [hier Takt in Hz]UL&#039;&#039; einzufügen. Bei Nutzung von delay.h ist darauf zu achten, dass die Optimierung des Compilers nicht ausgeschaltet ist, sonst wird sehr viel Code erzeugt und die Wartezeit stimmt nicht mit der gewünschten Zeitspanne überein.&lt;br /&gt;
Vgl. dazu  den [http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html entsprechenden Abschnitt der Dokumentation].&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff: (sollte nur noch in Ausnahmefällen genutzt werden)&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
Die Verwendung von Makefiles bietet noch viele weiter Möglichkeiten, einige davon werden im Anhang [[AVR-GCC-Tutorial#Zus.C3.A4tzliche_Funktionen_im_Makefile|Zusätzliche Funktionen im Makfile]] erläutert.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige (Integer) Datentypen =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei stdint.h definiert. &lt;br /&gt;
Zur Nutzung der standardisierten Typen bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// ab avr-libc Version 1.2.0 möglich und empfohlen:&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// veraltet: #include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef signed char        int8_t;&lt;br /&gt;
typedef unsigned char      uint8_t;&lt;br /&gt;
&lt;br /&gt;
typedef short              int16_t;&lt;br /&gt;
typedef unsigned short     uint16_t;&lt;br /&gt;
&lt;br /&gt;
typedef long               int32_t;&lt;br /&gt;
typedef unsigned long      uint32_t;&lt;br /&gt;
&lt;br /&gt;
typedef long long          int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* int8_t steht für einen 8-Bit Integer mit einem Wertebereich -128 bis +127.&lt;br /&gt;
* uint8_t steht für einen 8-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 255&lt;br /&gt;
&lt;br /&gt;
* int16_t steht für einen 16-Bit Integer mit einem Wertebereich -32768 bis +32767.&lt;br /&gt;
* uint16_t steht für einen 16-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 65535.&lt;br /&gt;
&lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; werden vorzeichenbehaftete Zahlen abgespeichert. Typen mit vorgestelltem &#039;&#039;u&#039;&#039; dienen der Ablage von postiven Zahlen (inkl. 0).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/(Standard) Integer Types&lt;br /&gt;
&lt;br /&gt;
= Bitfelder =&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&lt;br /&gt;
&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in eine einzelne Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitfelder sparen Platz im RAM, zu Lasten von Platz im Flash, verschlechtern aber unter Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.&lt;br /&gt;
&lt;br /&gt;
Wenn man nur eine paar wenige Variablen vom Typ bool verwenden möchte, kann man&lt;br /&gt;
auch die Headerdatei &amp;lt;stdbool.h&amp;gt; einbinden und sich dann wie gewohnt einen Booltyp anlegen. Variablen dieses Typs brauchen dennoch 1 Byte Speicher, ermöglichen aber eine genaue Unterscheidung zwischen Zahlenvariable und boolscher Variable.&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;C-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum können für allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden&lt;br /&gt;
AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039;, wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output).  Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt.&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend&lt;br /&gt;
angepasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O-Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek (avr-libc) stellt auch Funktionen zur Abfrage eines einzelnen Bits eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist, wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind &#039;&#039;nicht erforderlich&#039;&#039;, man kann auch &amp;quot;einfache&amp;quot; C-Syntax verwenden, die universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) wenn das Bit gesetzt und 0 (&amp;quot;falsch&amp;quot;) wenn es nicht gesetzt ist. Die Negierung des Ausdrucks, also  &#039;&#039;&#039;!&#039;&#039;&#039;(Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)), entspricht &#039;&#039;bit_is_clear&#039;&#039; und gibt einen Wert &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) zurück, falls das Bit nicht gesetzt ist,&lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O-Register einfach wie eine Variable setzen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und outp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben von Bits ====&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausdruck:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit (für 1) eines Bytes hat die Bitnummer 0, das &amp;quot;höchstwertige&amp;quot; (für 128) die Nummer 7.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );    /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3);   /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die warten, bis ein bestimmter Zustand auf einem Bit erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend gewartet&amp;quot; wird. D.h., der Programmablauf bleibt an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den Watchdog ein, muss man darauf achten, dass dieser auch noch getriggert wird (zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Änderung: Habe PINx und PORTx vertauscht.PINx ist für Pull up und PORTx für high bzw low. Michael.L  - &lt;br /&gt;
&lt;br /&gt;
Was sollte diese Aenderung?? Den Rest des Abschnitts auch gelesen? Evtl. die hier nicht erlaeuterten Zusatzfunktion der neuen AVRs durcheinandergeworfen? Aenderung verworfen bis mehr Details.  m.thomas. &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
// Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
&lt;br /&gt;
DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
/* mehr Tipparbeit aber übersichtlicher: */&lt;br /&gt;
DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&lt;br /&gt;
/* Einige Compiler erlauben die Eingabe von Konstanten im &lt;br /&gt;
   Binärformat, z.B. gcc in WinAVR, NICHT aber aus&lt;br /&gt;
   GNU-gcc-Quellen selbst erstellte Compiler. Diese Schreib-&lt;br /&gt;
   weise also nicht nutzen, wenn Code mit anderen &lt;br /&gt;
   ausgetauscht werden soll. */&lt;br /&gt;
DDRB = 0b00011111;    /* direkte Zuweisung &lt;br /&gt;
                         - übersichtlicher &lt;br /&gt;
                         - nicht standardkonform &lt;br /&gt;
                         - selten portabel&lt;br /&gt;
                         - nur bei modifiziertem GCC */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
Weitere Beispiele:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
DDRB = 0xff; &lt;br /&gt;
// Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; DDB0 );&lt;br /&gt;
// Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( ( 1 &amp;lt;&amp;lt; DDB3 ) | ( 1&amp;lt;&amp;lt;DDB4) );&lt;br /&gt;
// Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB |= ( 1 &amp;lt;&amp;lt; DDB0) | ( 1 &amp;lt;&amp;lt; DDB3 );&lt;br /&gt;
// Alle Pins auf Eingang:&lt;br /&gt;
DDRB = 0x00;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&#039;&#039;&lt;br /&gt;
* &#039;&#039;zuerst die Bits im PORTx-Register setzen und&#039;&#039;&lt;br /&gt;
* &#039;&#039;anschließend die Datenrichtung auf Ausgang stellen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Daraus ergibt sich die Abfolge für einen Eingangspin:&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze PORTx: interner Pull-Up aktiv&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze DDRx: Ausgang &amp;quot;high&amp;quot;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bei der Reihenfolge erst DDRx und dann PORTx, kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;&#039;nicht&#039;&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, &#039;&#039;&#039;sondern&#039;&#039;&#039; Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.&amp;lt;/font&amp;gt; (Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände.)&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch.  Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVRs haben sogar an den meisten Pins softwaremäßig zuschaltbare interne Pull-Up Widerstände, welche wir natürlich auch verwenden können.&lt;br /&gt;
&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;DDC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== (Tasten-)Entprellung ====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim Schließen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&lt;br /&gt;
&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen als mehrfache Impulse gezählt wird. Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* bei alter avr-libc: #include &amp;lt;avr/delay.h&amp;gt; */      &lt;br /&gt;
&lt;br /&gt;
/* Einfache Funktion zum Entprellen eines Tasters */&lt;br /&gt;
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)&lt;br /&gt;
{&lt;br /&gt;
    if ( ! (*port &amp;amp; (1 &amp;lt;&amp;lt; pin)) )&lt;br /&gt;
    {&lt;br /&gt;
        /* Pin wurde auf Masse gezogen, 100ms warten   */&lt;br /&gt;
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz&lt;br /&gt;
        _delay_ms(50); &lt;br /&gt;
        if ( *port &amp;amp; (1 &amp;lt;&amp;lt; pin) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Anwender Zeit zum Loslassen des Tasters geben */&lt;br /&gt;
            _delay_ms(50);&lt;br /&gt;
            _delay_ms(50); &lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; PB0 );                 /* PIN PB0 auf Eingang (Taster)            */&lt;br /&gt;
    PORTB |= ( 1 &amp;lt;&amp;lt; PB0 );                 /* Pullup-Widerstand aktivieren            */&lt;br /&gt;
    ...&lt;br /&gt;
    if (debounce(&amp;amp;PINB, PB0))             /* Falls Taster an PIN PB0 gedrueckt..    */&lt;br /&gt;
        PORTD = PIND ^ ( 1 &amp;lt;&amp;lt; PD7 );  /* ..LED an Port PD7 an-&lt;br /&gt;
                                   bzw. ausschalten */&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesem Beispiel ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritische Anwendungen sollte man ein anderes Verfahren nutzen (z.B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).&lt;br /&gt;
&lt;br /&gt;
Zum Thema Entprellen siehe auch:&lt;br /&gt;
* Artikel [[Entprellung]]&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls ben&amp;amp;ouml;tigt, kann eine 16-Bit Variable auch recht einfach manuell in ihre zwei 8-Bit Bestandteile zerlegt werden. Folgendes Beispiel demonstriert dies anhand des pseudo- 16-Bit Registers UBRR.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
typedef union {&lt;br /&gt;
        uint16_t i16;&lt;br /&gt;
        struct {&lt;br /&gt;
                uint8_t i8l;&lt;br /&gt;
                uint8_t i8h;&lt;br /&gt;
        };&lt;br /&gt;
} convert16to8;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
convert16to8 baud;&lt;br /&gt;
baud.i16 = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
UBRRH = baud.i8h;&lt;br /&gt;
UBRRL = baud.i8l;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/*alternativ:*/&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint16_t wFoo16;&lt;br /&gt;
uint8_t bFooLow, bFooHigh;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
wFoo16   = 0xAA55;                 /* zu &amp;quot;zerlegende&amp;quot; 16Bit-Integer */&lt;br /&gt;
bFooHigh = (uint8_t)(wFoo16 &amp;gt;&amp;gt; 8); /* MS-Byte */&lt;br /&gt;
bFooLow  = (uint8_t)(wFoo16);      /* LS-Byte */&lt;br /&gt;
...&lt;br /&gt;
#define us0(Data) ((unsigned char *)(&amp;amp;Data))&lt;br /&gt;
#define us1(Data) ((unsigned char *)((&amp;amp;Data)+1))&lt;br /&gt;
bFooHigh = us1(wFoo16);&lt;br /&gt;
bFoolow  = us0(wFoo16);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen (z.B. ATmega8) teilen sich UBRRH und UCSRC die gleiche Memory-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL) welches Register tats&amp;amp;auml;chlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und &amp;amp;uuml;bergibt den Wert &#039;&#039;3&#039;&#039; und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und &amp;amp;uuml;bergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Bei einigen 16-bit Registern (insbesondere die der 16-bit Timer) erfolgen Schreibzugriffe über das sogenannte &#039;&#039;temporary Register&#039;&#039;. Die Reihenfolge der Zugriffe bestimmt, wann der Wert tatsächlich ins Register geschrieben wird. Typisch wird erst das High-Byte beschrieben (xxxH) und intern im temporary Register zwischengespeichert. Nachdem das Low-Byte (xxxL) geschrieben wurde, setzt der Controller mit diesem und dem im temporary Register zwischengespeicherten Wert für das High-Byte das 16-bit Register. Dabei ist zu beachten, dass intern nur ein temporary Register verfügbar ist, welches in Interruptroutinen mglw. mit einem anderen Wert überschrieben wird, wenn dort ebenfalls 16-bit Register beschrieben werden. avr-gcc/avr-libc berücksichtigen die korrekte Reihenfolge automatisch, wenn die Register mit ihrem &amp;quot;16-bit Label&amp;quot; (ohne H bzw. L) angesprochen werden, dabei ist der Schutz des temporary Registers vor Überschreiben durch Interruptroutinen dennoch zu beachten (im Zweifel beim Schreibzugriff die Interrupts kurzzeitig global deaktivieren).&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t key_pressed(const volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if ( last_state == ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) ) ) {&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    i = key_pressed( &amp;amp;PINB, PB1 );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Im Übrigen können mit volatile gekennzeichnete Variablen auch als const deklariert werden, um sicherzustellen, dass sie nur noch von der Hardware änderbar sind.&lt;br /&gt;
&lt;br /&gt;
= Der UART =&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Über den [[UART]] kann ein AVR leicht mit einer [[RS-232]]-Schnittstelle eines PC oder sonstiger Geräte mit &amp;quot;serieller Schnittstelle&amp;quot; verbunden werden. &lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungen des UART:&lt;br /&gt;
&lt;br /&gt;
* Debug-Schnittstelle: z.B. zur Anzeige von Zwischenergebnissen (&amp;quot;printf-debugging&amp;quot; - hier besser &amp;quot;UART-debugging&amp;quot;) auf einem PC. Auf dem Rechner reicht dazu ein [[RS-232#Terminalprogramme|Terminalprogramm]] (MS-Windows: Hyperterm oder besser Bray-Terminal, [http://www.mikrocontroller.net/forum/read-8-155472.html#new HTerm]; Unix/Linux z.B. minicom). Ein direkter Anschluss ist aufgrund unterschiedlicher Pegel nicht möglich, jedoch sind entsprechende Schnittstellen-ICs wie z.B. ein MAX232 günstig und leicht zu integrieren. Rechner ohne serielle Schnittstelle können über fertige USB-seriell-Adapter angeschlossen werden. &lt;br /&gt;
* &amp;quot;Mensch-Maschine Schnittstelle&amp;quot;: z.B. Konfiguration und Statusabfrage über eine &amp;quot;Kommandozeile&amp;quot; oder Menüs (siehe z.B. Forumsbeitrag [http://www.mikrocontroller.net/topic/52985 Auswertung RS232-Befehle]) &lt;br /&gt;
* Übertragen von gespeicherten Werten: z.B. bei einem Datenlogger&lt;br /&gt;
* Anschluss von Geräten mit serieller Schnittstelle (z.B. (Funk-)Modems, Mobiltelefone, Drucker, Sensoren, &amp;quot;intelligente&amp;quot; LC-Displays, GPS-Empfänger). &lt;br /&gt;
* &amp;quot;Feldbusse&amp;quot; auf RS485/RS422-Basis mittels entsprechenden Bustreiberbausteinen (z.B. MAX485)&lt;br /&gt;
* DMX, Midi etc.&lt;br /&gt;
* LIN-Bus (&#039;&#039;&#039;L&#039;&#039;&#039;ocal &#039;&#039;&#039;I&#039;&#039;&#039;nterconnect &#039;&#039;&#039;N&#039;&#039;&#039;etwork): Preiswerte Sensoren/Aktoren in der Automobiltechnik und darüber hinaus&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben ein bis zwei vollduplexfähigen UART (&#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) schon eingebaut (&amp;quot;Hardware-UART&amp;quot;). &lt;br /&gt;
Übrigens: Vollduplex heißt nichts anderes, als dass der Baustein gleichzeitig senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega, ATtiny) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterscheidet sich vom UART hauptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurationsregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit zeigt an, ob der Sendepuffer bereit ist, um ein zu sendendes Zeichen aufzunehmen. Das Bit wird vom AVR gesetzt (1), wenn der Sendepuffer leer ist. Es wird gelöscht (0), wenn ein Zeichen im Sendedatenregister vorhanden ist und noch nicht in das Sendeschieberegister übernommen wurde. Atmel empfiehlt aus Kompatibilitätsgründen mit kommenden µC, UDRE auf 0 zu setzen, wenn das UCSRA Register beschrieben wird.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
\mathrm{UBRR} = \frac{\mathrm{Taktfrequenz}}{\mathrm{Baudrate} \cdot 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS-232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. &lt;br /&gt;
&amp;lt;!-- &amp;quot;Hackerloesung&amp;quot; auskommentiert - nicht so gut in einem &amp;quot;Einsteiger-Tutorial&amp;quot; - mthomas&lt;br /&gt;
Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfiehlt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== UART initialisieren ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben. Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach gewünschter Funktionsweise die benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Da wir vorerst nur senden möchten und noch keine Interrupts auswerten wollen, gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs mit USART haben mehrere Konfigurationsregister und erfordern eine etwas andere Konfiguration. Für einen ATmega16 z.B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                 // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);     // Asynchron 8N1&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist noch das Baudratenregister &#039;&#039;&#039;UBRR&#039;&#039;&#039; einzustellen. Bei neueren AVRs besteht es aus zwei Registern &#039;&#039;&#039;UBRRL&#039;&#039;&#039; und &#039;&#039;&#039;UBRRH&#039;&#039;&#039;. Der Wert dafür ergibt sich aus der angegebenen Formel durch Einsetzen der Taktfrequenz und der gewünschten Übertragungsrate. Das Berechnen der Formel wird dem [[C-Präprozessor|Präprozessor]] überlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann&lt;br /&gt;
   F_CPU im Makefile definiert werden, eine nochmalige Definition&lt;br /&gt;
   hier wuerde zu einer Compilerwarnung fuehren. Daher &amp;quot;Schutz&amp;quot; durch&lt;br /&gt;
   #ifndef/#endif &lt;br /&gt;
&lt;br /&gt;
   Dieser &amp;quot;Schutz&amp;quot; kann zu Debugsessions führen, wenn AVRStudio &lt;br /&gt;
   verwendet wird und dort eine andere, nicht zur Hardware passende &lt;br /&gt;
   Taktrate eingestellt ist: Dann wird die folgende Definition &lt;br /&gt;
   nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) &lt;br /&gt;
   von AVRStudio - daher Ausgabe einer Warnung falls F_CPU&lt;br /&gt;
   noch nicht definiert: */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000&amp;quot;&lt;br /&gt;
#define F_CPU 4000000UL    // Systemtakt in Hz &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600UL          // Baudrate&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) // Fehler in Promille, 1000 = kein Fehler.&lt;br /&gt;
&lt;br /&gt;
#if ((BAUD_ERROR&amp;lt;990) || (BAUD_ERROR&amp;gt;1010))&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;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    UBRR = UBRR_VAL;&lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&amp;lt;!-- mthomas: warum nicht UL?, wird von AVRStudio auch mit UL übergeben --&amp;gt;&lt;br /&gt;
Wieder für den Mega16 mit zwei Registern für die Baudrateneinstellung eine etwas andere Programmierung. Wichtig ist, dass UBRRH &#039;&#039;&#039;vor&#039;&#039;&#039; UBRRL geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmega16 */&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einige AVR (z.B. ATmega169, ATmega48/88/168, AT90CAN jedoch nicht für z.B. ATmega16/32, ATmega128, ATtiny2313) wird durch die Registerdefinitionen der avr-libc (io*.h) auch für Controller mit zwei UBRR-Registern (UBRRL/UBRRH) ein UBRR bzw. UBRR0 als &amp;quot;16-bit-Register&amp;quot; definiert und man kann auch Werte direkt per UBRR = UBRR_VAL zuweisen. Intern werden dann zwei Zuweisungen für UBRRH und UBRRL generiert. Dies ist nicht bei allen Controllern möglich, da die beiden Register nicht bei allen aufeinanderfolgende Addressen aufweisen. Die getrennte Zuweisung an UBRRH und UBRRL wie im Beispiel gezeigt ist universeller und portabler und daher vorzuziehen.&lt;br /&gt;
&lt;br /&gt;
Die Makros sind sehr praktisch, da sie sowohl automatisch den Wert für UBRR als auch den Fehler in der generierten Baudrate berechnen und im Falle einer Überschreitung (+/-1%) einen Fehler und somit Abbruch im Compilerablauf generieren. Damit können viele Probleme mit &amp;quot;UART sendet komische Zeichen&amp;quot; vermieden werden. Ausserdem kann man mühelos die Einstellung an eine neue Taktfrequenz bzw. Baudrate anpassen, ohne selber rechnen oder in Tabellen nachschlagen zu müssen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. Vorher ist zu prüfen, ob das UART-Modul bereit ist, das zu sendende Zeichen entgegenzunehmen. Die Bezeichnungen des/der Statusregisters mit dem Bit UDRE ist abhängig vom Controllertypen (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    // bei AVR mit einem UART (&amp;quot;classic AVR&amp;quot; z.B. AT90S8515)&lt;br /&gt;
    while (!(USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                  /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&lt;br /&gt;
    /** ODER **/&lt;br /&gt;
&lt;br /&gt;
    // bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                    /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe &amp;quot;String senden&amp;quot; wird durch zwei Funktionen abgearbeitet. Die universelle/controllerunabhängige Funktion uart_puts übergibt jeweils ein Zeichen der Zeichenkette an eine Funktion uart_putc, die abhängig von der vorhandenen Hardware implementiert werden muss. In der Funktion zum Senden eines Zeichens ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// putc fuer AVR mit einem UART (z.B. AT90S8515)&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while(!(USR &amp;amp; (1 &amp;lt;&amp;lt; UDRE)))  /* warte, bis UDR bereit */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = c;                     /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** ODER **/&lt;br /&gt;
&lt;br /&gt;
// bei neueren AVRs andere Bezeichnung fuer die Statusregister, hier ATmega16:&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich */&lt;br /&gt;
    {&lt;br /&gt;
    }                             &lt;br /&gt;
&lt;br /&gt;
    UDR = c;                      /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* puts ist unabhaengig vom Controllertyp */&lt;br /&gt;
void uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != &#039;\0&#039; also ungleich dem &amp;quot;String-Endezeichen&amp;quot; */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die in uart_putc verwendeten Schleifen, in denen gewartet wird bis die UART-Hardware zum senden bereit ist, sind insofern etwas kritisch, da während des Sendens eines Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. empfangenen Zeichen/Bytes zwischengespeichert und in Interruptroutinen an die U(S)ART-Hardware weitergegeben bzw. von ihr ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&lt;br /&gt;
&lt;br /&gt;
=== Schreiben von Variableninhalten ===&lt;br /&gt;
&lt;br /&gt;
Sollen Inhalte von Variablen (Ganzzahlen, Fließkomma) in &amp;quot;menschenlesbarer&amp;quot; Form gesendet werden, ist vor dem Transfer eine Umwandlung in Zeichen (&amp;quot;ASCII&amp;quot;) erforderlich. Bei nur einer Ziffer ist diese Umwandlung relativ einfach: man addiert den ASCII-Wert von Null zur Ziffer und kann diesen Wert direkt senden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_putc (s.o.)&lt;br /&gt;
&lt;br /&gt;
// Ausgabe von 0123456789&lt;br /&gt;
char c;&lt;br /&gt;
&lt;br /&gt;
for (uint8_t i=0; i&amp;lt;=9; ++i) {&lt;br /&gt;
    c = i + &#039;0&#039;;&lt;br /&gt;
    uart_putc( c );&lt;br /&gt;
    // verkuerzt: uart_putc( i + &#039;0&#039; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll mehr als eine Ziffer ausgegeben werden, bedient man sich zweckmäßigerweise vorhandener Funktionen zur Umwandlung von Zahlen in Zeichenketten/Strings. Die Funktion der avr-libc zur Umwandlung von vorzeichenbehafteten 16bit-Ganzzahlen (int16_t) in Zeichenketten heißt &#039;&#039;itoa&#039;&#039; (Integer to ASCII). Man muss der Funktion einen Speicherbereich zur Verarbeitung (buffer) mit Platz für alle Ziffern, das String-Endezeichen (&#039;\0&#039;) und evtl. das Vorzeichen bereitstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   char s[7];&lt;br /&gt;
   int16_t i = -12345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   itoa( i, s, 10 ); // 10 fuer radix -&amp;gt; Dezimalsystem&lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // da itoa einen Zeiger auf den Beginn von s zurueckgibt verkuerzt auch:&lt;br /&gt;
   uart_puts( itoa( i, s, 10 ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für vorzeichenlose 16bit-Ganzzahlen (uint16_t) exisitert &#039;&#039;utoa&#039;&#039;. Die Funktionen für 32bit-Ganzzahlen (int32_t und uint32_t) heißen &#039;&#039;ltoa&#039;&#039; bzw. &#039;&#039;ultoa&#039;&#039;. Da 32bit-Ganzzahlen mehr Stellen aufweisen können, ist ein entsprechend größerer Pufferspeicher vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Auch Fließkommazahlen (float/double) können mit breits vorhandenen Funktionen in Zeichenfolgen umgewandelt werden, dazu existieren die Funktionen &#039;&#039;dtostre&#039;&#039; und &#039;&#039;dtostrf&#039;&#039;. dtostre nutzt Exponentialschreibweise (&amp;quot;engineering&amp;quot;-Format). (Hinweis: z.Zt. existiert im avr-gcc kein &amp;quot;echtes&amp;quot; double, intern wird immer mit &amp;quot;einfacher Genauigkeit&amp;quot;, entsprechend float, gerechnet.) &lt;br /&gt;
&lt;br /&gt;
dtostrf und dtostre benötigen die libm.a der avr-libc. Bei Nutzung von Makefiles ist der Parameter -lm in in LDFLAGS anzugeben (Standard in den WinAVR/mfile-Makefilevorlagen). Nutzt man AVRStudio als IDE für den GNU-Compiler (gcc-Plugin) ist die libm.a unter Libaries auszuwählen: Project -&amp;gt; Configurations Options -&amp;gt; Libaries -&amp;gt; libm.a mit dem Pfeil nach rechts einbinden. Siehe auch die [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|FAQ]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
/* lt. avr-libc Dokumentation:&lt;br /&gt;
char* dtostrf(&lt;br /&gt;
  double __val,&lt;br /&gt;
  char   __width,&lt;br /&gt;
  char   __prec,&lt;br /&gt;
  char * __s&lt;br /&gt;
)  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   // Pufferspeicher ausreichend groß&lt;br /&gt;
   // evtl. Vorzeichen + width + Endezeichen:&lt;br /&gt;
   char s[8]; &lt;br /&gt;
   float f = -12.345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   dtostrf( f, 6, 3, s ); &lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // verkürzt: uart_puts( dtostrf( f, 7, 3, s ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfangen ==&lt;br /&gt;
=== einzelne Zeichen empfangen ===&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von Zeichen muss der Empfangsteil des UART bei der Initialisierung aktiviert werden, indem das RXEN-Bit im jeweiligen Konfigurationsregister (UCSRB bzw UCSR0B/UCSR1B) gesetzt wird. Im einfachsten Fall wird solange gewartet, bis ein Zeichen empfangen wurde, dieses steht dann im UART-Datenregister (UDR bzw. UDR0 und UDR1 bei AVRs mit 2 UARTS) zur Verfügung (sogen. &amp;quot;Polling-Betrieb&amp;quot;). Ein Beispiel für den ATmega16:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Zusaetzlich zur Baudrateneinstellung und der weiteren Initialisierung: */&lt;br /&gt;
void Usart_EnableRX()&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= ( 1 &amp;lt;&amp;lt; RXEN );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion blockiert den Programmablauf. Alternativ kann das RXC-Bit in einer Programmschleife abgefragt werden und dann nur bei gesetztem RXC-Bit UDR ausgelesen werden. Eleganter und in den meisten Anwendungsfällen &amp;quot;stabiler&amp;quot; ist die Vorgehensweise, die empfangenen Zeichen in einer Interrupt-Routine einzulesen und zur späteren Verarbeitung in einem Eingangsbuffer (FIFO-Buffer) zwischenzuspeichern. Dazu existieren fertige und gut getestete [[Libraries|Bibliotheken]] &amp;lt;!-- &amp;quot;echte Libraries&amp;quot; (.a) wie im Verweis beschrieben sind hier eigentlich nicht gemeint, verwirrt hier etwas, da AVR-&amp;quot;Libraries&amp;quot; meist per #defines anpassbare Source-Codes sind, vielleicht so: --&amp;gt; und Quellcodekomponenten (z.B. UART-Library von P. Fleury, procyon-avrlib und einige in der &amp;quot;Academy&amp;quot; von avrfreaks.net).&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html Dokumenation der avr-libc/stdlib.h]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_printf Die Nutzung von printf]&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&amp;lt;!-- nimmermehr: * siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (printf etc.) im [[AVR-Tutorial:_UART]]. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: 9bit&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Zeichenketten (Strings) ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Zeichenketten, muß man sich zunächst darüber im klaren sein, daß es ein Kriterium geben muß, an dem der µC erkennen kann, wann ein String zu Ende ist. Sehr oft wird dazu das Zeichen &#039;Return&#039; benutzt, um das Ende eines Strings zu markieren. Dies ist vom Benutzer einfach eingebbar und er ist auch daran gewöhnt, daß er eine Eingabezeile mit einem Druck auf die Return Taste abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Prinzipiell gibt es jedoch keine Einschränkung bezüglich dieses speziellen Zeichens. Es muß nur sichergestellt werden, daß dieses spezielle &#039;Ende eines Strings&#039; - Zeichen nicht mit einem im Text vorkommenden Zeichen verwechselt werden kann. Wenn also im zu übertragenden Text beispielsweise kein &#039;;&#039; vorkommt, dann spricht nichts dagegen, einen String mit einem &#039;;&#039; abschließen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird die durchaus übliche Annahme getroffen, daß eine Stringübertragung identisch ist mit der Übertragung einer Textzeile und daher mit einem Return (&#039;\n&#039;) abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Das Problem der Übertragung eines Strings reduziert sich damit auf die Aufgabenstellung: Empfange und Sammle Zeichen in einem char Array, bis entweder das Array voll ist oder das &#039;String Ende Zeichen&#039; empfangen wurde. Danach wird der empfangene Text noch mit einem &#039;\0&#039; Zeichen abgeschlossen um einen Standard C-String daraus zu machen, mit dem dann weiter gearbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_gets( char* Buffer, uint8_t MaxLen )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t NextChar;&lt;br /&gt;
  uint8_t StringLen = 0;&lt;br /&gt;
&lt;br /&gt;
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen&lt;br /&gt;
&lt;br /&gt;
                                  // Sammle solange Zeichen, bis:&lt;br /&gt;
                                  // * entweder das String Ende Zeichen kam&lt;br /&gt;
                                  // * oder das aufnehmende Array voll ist&lt;br /&gt;
  while( NextChar != &#039;\n&#039; &amp;amp;&amp;amp; StringLen &amp;lt; MaxLen - 1 ) {&lt;br /&gt;
    *Buffer++ = NextChar;&lt;br /&gt;
    StringLen++;&lt;br /&gt;
    NextChar = uart_getc();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
                                  // Noch ein &#039;\0&#039; anhängen um einen Standard&lt;br /&gt;
                                  // C-String daraus zu machen&lt;br /&gt;
  *Buffer = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf ist darauf zu achten, dass das empfangende Array auch mit einer&lt;br /&gt;
vernünftigen Größe definiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
  char Line[40];      // String mit maximal 39 zeichen&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) );&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Interruptbetrieb==&lt;br /&gt;
&lt;br /&gt;
Beim ATMEGA8 ist es möglich, dass RXCIE Bit im Register UCSRB zu setzen.&lt;br /&gt;
Dieser Interrupt wird immer ausglöst, wenn Daten erfolgreich empfangen wurden.&lt;br /&gt;
Zusätzlich braucht man die Routine:&lt;br /&gt;
&lt;br /&gt;
ISR (USART_RXC_vect) {&lt;br /&gt;
   //irgendein Code&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
natürlich muss &amp;quot;Global Interrupt Enable&amp;quot; Aktiviert sein.&lt;br /&gt;
!! Nur getestet beim ATMEGA8 !!&lt;br /&gt;
&lt;br /&gt;
[BAUSTELLE! Aus Lerngründen eventuell als eigenen UART-Interrupt-Block hinter den grundlegenden Interrupt-Teil im Tutorial und hier eine kurze Einführung und einen Verweis darauf anbieten.]&lt;br /&gt;
&lt;br /&gt;
* Unterschied Polling-Betrieb (bisher, oben) und Interrupt-Betrieb&lt;br /&gt;
* Empfangen (Receive)&lt;br /&gt;
** Verändertes UART-Init, ISR (RXC), ggf. Fallstricke ([http://www.mikrocontroller.net/topic/84256#707214 UDR in der ISR lesen!]), Philosophie einer ISR (kurz und schmerzlos), Datenaustausch ISR zu Restprogramm (volatile)&lt;br /&gt;
** Einfachstbeispiel ([http://www.mikrocontroller.net/topic/84228#707052 Echo] (noch buggy beim Datenzugriff, siehe Lit. 2+3!)), ggf. LED zur ISR-Empfangsanzeige oder Overflow-Anzeige&lt;br /&gt;
* FIFO-Puffer, Ringpuffer&lt;br /&gt;
* Senden (Transmit)&lt;br /&gt;
** Variante &amp;quot;UART Data Register Empty&amp;quot; (UDRE) &lt;br /&gt;
** Variante &amp;quot;UART Transmit Complete&amp;quot; (TXC) &lt;br /&gt;
* Fertige UART-Bibliotheken (-&amp;gt;Fleury, -&amp;gt;Procyon)&lt;br /&gt;
* Literatur: &lt;br /&gt;
** 1/ [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=48188 avrfreaks.net Tutorial] inkl. Diskussion (engl.)&lt;br /&gt;
** 2/ avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_16bitio Why do some 16-bit timer registers sometimes get trashed?]&lt;br /&gt;
** 3/ [[Interrupt]] und atomarer Datenzugriff&lt;br /&gt;
&lt;br /&gt;
==Software-UART==&lt;br /&gt;
&lt;br /&gt;
Falls die Zahl der vorhandenen Hardware-UARTs nicht ausreicht, können weitere Schnittstellen über sogennante Software-UARTs ergänzt werden. Es gibt dazu (mindestens) zwei Ansätze: &lt;br /&gt;
* Der bei AVRs üblichste Ansatz basiert auf dem Prinzip, dass ein externer Interrupt-Pin für den Empfang (&amp;quot;RX&amp;quot;) genutzt wird. Das Startbit löst den Interrupt aus, in der Interrupt-Routine (ISR) wird der externe Interrupt deaktiviert und ein Timer aktiviert. In der Interrupt-Routine des Timers wird der Zustand des Empfangs-Pins entsprechend der Baudrate abgetastet. Nach Empfang des Stop-Bits wird der externe Interrupt wieder aktiviert. Senden kann über einen beliebigen Pin (&amp;quot;TX&amp;quot;) erfolgen, der entsprechend der Baudrate und dem zu sendenden Zeichen auf 0 oder 1 gesetzt wird. Die Implementierung ist nicht ganz einfach, es existieren dazu aber fertige Bibliotheken (z.B. bei [http://www.avrfreaks.net/ avrfreaks] oder in der [http://hubbard.engr.scu.edu/embedded/avr/avrlib/ Procyon avrlib]).&lt;br /&gt;
* Ein weiterer Ansatz erfordert keinen Pin mit &amp;quot;Interrupt-Funktion&amp;quot; aber benötigt mehr Rechenzeit. Jeder Input-Pin kann als Empfangspin (RX) dienen. Über einen Timer wird der Zustand des RX-Pins mit einem vielfachen der Baudrate abgetastet (dreifach scheint üblich) und High- bzw. Lowbits anhand einer Mindestanzahl identifiziert. (Beispiel: &amp;quot;Generic Software Uart&amp;quot; Application-Note von IAR)&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (z.B. ATtiny26 oder ATmega48,88,168,169) verfügen über ein Universal Serial Interface (USI), das teilweise UART-Funktion übernehmen kann. Atmel stellt eine Application-Note bereit, in der die Nutzung des USI als UART erläutert wird (im Prinzip &amp;quot;Hardware-unterstützter Software-UART&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==Handshaking==&lt;br /&gt;
Wenn der Sender ständig sendet, wird irgendwann der Fall eintreten, daß der Empfänger nicht bereit ist, neue Zeichen zu empfangen. In diesem Fall muß durch ein &#039;&#039;&#039;Handshake-Verfahren&#039;&#039;&#039; die Situation bereinigt werden. Handshake bedeutet nichts anderes, als daß der Empfänger dem Sender mitteilt, daß er zur Zeit keine Daten annehmen kann und der Sender die Übertragung der nächsten Zeichen solange einstellen soll, bis der Empfänger signalisiert, daß er wieder Zeichen aufnehmen kann.&lt;br /&gt;
===Hardwarehandshake (RTS/CTS)===&lt;br /&gt;
Beim Hardwarehandshake werden zusätzlich zu den beiden Daten-Übertragungsleitungen noch 2 weitere Leitungen benötigt: &#039;&#039;&#039;RTS&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;equest &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end) und &#039;&#039;&#039;CTS&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end). Jeder der beiden Kommunikationspartner ist verpflichtet, bevor ein Zeichen gesendet wird, den Zustand der &#039;&#039;&#039;RTS&#039;&#039;&#039; Leitung zu überprüfen. Nur wenn die Gegenstelle darauf Empfangsbereitschaft signalisiert, darf das Zeichen gesendet werden. Um der Gegenstelle zu signalisieren, daß sie zur Zeit keine Zeichen schicken soll, wird die Leitung &#039;&#039;&#039;CTS&#039;&#039;&#039; benutzt.&lt;br /&gt;
&lt;br /&gt;
===Softwarehandshake (XON/XOFF)===&lt;br /&gt;
Beim Softwarehandshake sind keine speziellen Leitungen notwendig. Statt dessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, daß Senden einzustellen bzw. wieder aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;XOFF&#039;&#039;&#039; Aufforderung das Senden einzustellen&lt;br /&gt;
* &#039;&#039;&#039;XON&#039;&#039;&#039;  Gegenstelle darf wieder senden&lt;br /&gt;
&lt;br /&gt;
Nachteilig bei einem Softwarehandshake ist es, daß dadurch keine direkte binäre Datenübertragung mehr möglich ist. Von den möglichen 256 Bytewerten werden ja 2 (nämlich &#039;&#039;&#039;XON&#039;&#039;&#039; und &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus.&lt;br /&gt;
&lt;br /&gt;
==Fehlersuche==&lt;br /&gt;
Erstaunlich oft wird im Forum der Hilferuf laut: &amp;quot;Meine UART funktioniert nicht, was mache ich falsch&amp;quot;. In der überwiegenden Mehrzahl der Fälle stellt sich dann heraus, daß es sich um ein Hardwareproblem handelt, wobei da wiederrum der Löwenanteil auf das Konto einer nicht korrekt eingestellten Taktrate geht: Der µC benutzt nicht einen angeschlossenen Quarz, so wie er auch im Programm eingetragen ist, sondern läuft immer noch mit dem internen RC-Takt. Daraus resultiert aber auch, daß der Baudraten Konfigurationswert falsch berechnet wird.&lt;br /&gt;
&lt;br /&gt;
Eine Checkliste zum Aufspüren solcher Fehler findet sich [[AVR_Checkliste#UART.2FUSART|hier]].&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Analoge Eingangswerte werden in der Regel über den AVR Analog-Digital-Converter (AD-Wandler, ADC) eingelesen, der in vielen Typen verfügbar ist (typisch 10bit Auflösung). Durch diesen werden analogen Signale (Spannungen) in digitale Zahlenwerte gewandelt. Bei AVRs, die über keinen internen AD-Wandler verfügen (z.B. ATmega162, ATtiny2313), kann durch externe Beschaltung (R/C-Netzwerk und &amp;quot;Zeitmessung&amp;quot;) die Funktion des AD-Wandlers &amp;quot;emuliert&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
Es existieren keine AVRs mit eingebautem Digital-Analog-Konverter (DAC). Diese Funktion muss durch externe Komponenten nachgebildet werden (z.B. PWM und &amp;quot;Glättung&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Unabhängig davon besteht natürlich immer die Möglichkeit, spezielle Bausteine zur Analog-Digital- bzw. Digital-Analog-Wandlung zu nutzen und diese über eine digitale Schnittstelle (z.b. SPI oder I2C) mit einem AVR anzusteuern.&lt;br /&gt;
&lt;br /&gt;
== AC (Analog Comparator) ==&lt;br /&gt;
&lt;br /&gt;
Der Comparator vergleicht 2 Spannungen an den Pins AIN0 und AIN1 und gibt einen Status aus welche der beiden Spannungen größer ist. AIN0 Dient dabei als Referenzspannung (Sollwert) und AIN1 als Vergleichsspannung (Istwert). Als Referenzspannung kann auch alternativ eine interne Referenzspannung ausgewählt werden.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Liegt die Vergleichsspannung (IST) unter der der Referenzspannung (SOLL) gibt der Comperator eine logische 0 aus. ISt die Vergleichsspannung hingegen größer als die Referenzspannung wird eine logische 1 ausgegeben.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Der Comperator arbeitet völlig autark bzw. parallel zum Prozessor. Für mobile Anwendungen empfiehlt es sich ihn abzuschalten sofern er nicht benötigt wird, da er ansonsten Strom benötigt. Der Comparator kann Interruptgesteuert abgefragt werden oder im Pollingbetrieb.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Steuer- bzw. Statusregister ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ACSR (0x28) - Analog Comparator Status Register&amp;lt;BR&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACD&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;AINBG&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACO&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACI&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | n/a&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 7 ACD - Analog Comperator Disable: 0 = Comparator ein, 1 = Comparator aus. Wird dieses Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 ACIE ggf. abgeschaltet werden.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 6 AINBG - Analog Comparator Bandgap Select: Ermöglicht das umschalten zwischen interner und externer Referenzspannung. 1 = interne (~1,3 Volt), 0 = externe Referenzspannung (an Pin AIN0)&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 5 ACO - Analog Coparator Output: Hier wird das Ergebnis des Vergleichs angezeigt. Es liegt typischerweise nach 1-2 Taktzyklen vor. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ergebnis: &amp;lt;BR&amp;gt;IST &amp;lt; SOLL --&amp;gt; 0&amp;lt;BR&amp;gt;&lt;br /&gt;
IST &amp;gt; SOLL --&amp;gt; 1&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4 ACI - Analog Comparator Interrupt Flag: Dieses Bit wird von der Hardware gesetzt wenn ein Interruptereignis das in Bit 0 und 1 definiert ist eintritt. Dieses Bit löst noch keinen Interrupt aus! Die Interruptroutine wird nur dann ausgeführt wenn das Bit 3 ACIE gesetzt ist und global Interrupts erlaubt sind (I-Bit in SREG=1). Das Bit 4 ACI wird wieder gelöscht wenn die Interruptroutine ausgeführt wurde oder wenn manuell das Bit auf 1 gesetzt wird. Das Bit kann für Abfragen genutzt werden, steuert oder konfuguriert aber nicht den Comparator.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 3 ACIE - Analog Comparator Interrupt Enable: Ist das Bit auf 1 gesetzt wird immer ein Interrupt ausgelöst wenn das Ereignis das in Bit 1 und 0 definiert ist eintritt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 2 ACIC - Analog Comparator Input Capture Enable: Wird das Bit gesetzt wird der Comparatorausgang intern mit dem Counter 1 verbunden. Es könnten damit z.b. die Anzahl der Vergleiche im Counter1 gezählt werden. Um den Comparator an den Timer1 Input Capture Interrupt zu verbinden muß im Timerregister das TICIE1 Bit auf 1 gesetzt werden. Der Trigger wird immer dann ausgelöst wenn das in Bit 1 und 0 definierte Ereignis eintritt.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 1,0 ACIS1,ACIS0 - Analog Comparator Interrupt select: Hier wird definiert welche Ereignisse einen Interrupt auslösen sollen:&amp;lt;BR&amp;gt;&lt;br /&gt;
00 = Interrupt auslösen bei jedem Flankenwechsel&amp;lt;BR&amp;gt;&lt;br /&gt;
10 = Interrupt auslösen bei fallender Flanke&amp;lt;BR&amp;gt;&lt;br /&gt;
11 = Interrupt auslösen bei steigender Flanke&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Werden diese Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 gelöscht werden.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der Analog-Digital-Konverter (ADC) wandelt analoge Signale in digitale Werte um, welche vom Controller interpretiert werden können. Einige AVR-Typen haben bereits einen mehrkanaligen Analog-Digital-Konverter eingebaut. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst werden kann, wird durch die Auflösung des ADC in Anzahl Bits angegeben, man hört bzw. liest jeweils von 8-Bit-ADC oder 10-Bit-ADC oder noch höher. ADCs die in AVRs enthalten sind haben zur Zeit eine maximale Auflösung von 10-Bit.&lt;br /&gt;
&lt;br /&gt;
Ein ADC mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des Analog-Digital-Konverters ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Der interne ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem Analog-Digital-Wandler (ADC) zurückgreifen, die über mehrere Kanäle verfügen. Kanäle heißt in diesem Zusammenhang, dass zwar bis zu zehn analoge Eingänge am AVR verfügbar sind, aber nur ein &amp;quot;echter&amp;quot; Analog-Digital-Wandler zur Verfügung steht, vor der eigentlichen Messung ist also einzustellen, welcher Kanal (&amp;quot;Pin&amp;quot;) mit dem Wandler verbunden und gemessen wird.&lt;br /&gt;
&lt;br /&gt;
Die Umwandlung innerhalb des AVR basiert auf der schrittweisen Näherung. Beim AVR müssen die Pins &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AVCC&#039;&#039;&#039; beschaltet werden. Für genaue Messungen sollte AVCC über ein L-C Netzwerk mit VCC verbunden werden, um Spannungsspitzen und -einbrüche vom Analog-Digital-Wandler fernzuhalten. Im Datenblatt findet sich dazu eine Schaltung, die 10uH und 100nF vorsieht.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Analog-Digital-Wandlung wird auf eine Referenzspannung bezogen. Aktuelle AVRs bieten drei Möglichkeiten zur Wahl dieser Spannung:&lt;br /&gt;
&lt;br /&gt;
* Eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; am Anschlusspin &#039;&#039;&#039;AREF&#039;&#039;&#039;. Die minimale (externe) Referenzspannung darf jedoch nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. &lt;br /&gt;
&lt;br /&gt;
* Verfügt der AVR über eine interne Referenzspannung, kann diese genutzt werden. Alle aktuellen AVRs mit internem AD-Wandler sollten damit ausgestattet sein (vgl. Datenblatt: 2,56V oder 1,1V je nach Typ). Das Datenblatt gibt auch über die Genauigkeit dieser Spannung Auskunft.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;interne&#039;&#039;&#039; Referenzspannung wird auf &#039;&#039;&#039;Vcc&#039;&#039;&#039; bezogen, eine externe Referenzspannung auf &#039;&#039;&#039;GND (Masse)&#039;&#039;&#039;. Davon unabhängig werden digitalisierte Spannungen &#039;&#039;&#039;immer&#039;&#039;&#039; auf GND bezogen. Beim ATMEGA8 z.B. ist der Pin AREF über 32kOhm mit GND verbunden, d.h. man muss diese doch extrem niedrige Eingangsimpedanz mit in die Berechnung für einen Spannungsteiler einbeziehen, bzw. kann diesen Widerstand als R2 gleich mit benutzen. Formel für Spannungsteiler: Udiv = U / ((R1 + R2) / R2)&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung von Vcc oder der internen Referenz wird empfohlen, einen Kondensator zwischen dem AREF-Pin und GND anzuordnen. Die Festlegung, welche Spannungsreferenz genutzt wird, erfolgt z.B. beim ATmega16 mit den Bits REFS1/REFS0 im ADMUX-Register. Die zu messende Spannung muss im Bereich zwischen &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AREF&#039;&#039;&#039; (egal ob intern oder extern) liegen. &lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; kann in zwei verschiedenen Betriebsarten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
; Einfache Wandlung (Single Conversion) : In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestoßen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
; Frei laufend (Free Running) : In dieser Betriebsart erfasst der Wandler permanent die anliegende Spannung und schreibt diese in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Die Register des ADC ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; verfügt über eigene Register. Im Folgenden die Registerbeschreibung eines  ATMega16, welcher über 8 ADC-Kanäle verfügt. Die Register unterscheiden sich jedoch nicht erheblich von denen anderer AVRs (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister A.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den &#039;&#039;&#039;ADC&#039;&#039;&#039; verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADEN&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able)&lt;br /&gt;
:Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren. Wenn das Bit nicht gesetzt ist, können die Pins wie normale I/O-Pins verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADSC&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion)&lt;br /&gt;
:Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.&lt;br /&gt;
:Wenn das Bit nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.&lt;br /&gt;
:Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADFR&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;F&#039;&#039;&#039;ree &#039;&#039;&#039;R&#039;&#039;&#039;un select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Ist das Bit auf 1 gesetzt arbeitet der ADC im Freerunning Modus. Dabe iwird das Datenregister permanent aktualisiert. Ist das Bit hingegen auf 0 gesetzt macht der ADC nur eine Single Conversion. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIF&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag)&lt;br /&gt;
:Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt, sobald eine Umwandlung erfolgt ist und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert wurde. Das Bit wird bei lesendem Zugriff auf &#039;&#039;&#039;ADC(L,H)&#039;&#039;&#039; automatisch (d.h. durch die Hardware) gelöscht.&lt;br /&gt;
:Wenn das &#039;&#039;&#039;ADIE&#039;&#039;&#039; Bit sowie das &#039;&#039;&#039;I-Bit&#039;&#039;&#039; im AVR &#039;&#039;&#039;Statusregister&#039;&#039;&#039; gesetzt ist, wird der &#039;&#039;&#039;ADC Interrupt&#039;&#039;&#039; ausgelöst und die Interrupt-Behandlungsroutine aufgerufen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn die Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 in das Register geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIE&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;, dann wird der &#039;&#039;&#039; ADC-Interrupt&#039;&#039;&#039; aktiviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADPS2...ADPS0&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;P&#039;&#039;&#039;rescaler &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
:Der &#039;&#039;&#039;ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz ergibt.&lt;br /&gt;
:Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
TF_{min}=\frac{CLK}{200\,\mathrm{kHz}}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50\,\mathrm{kHz}}=\frac{4000000}{50000}=\mathbf{80}&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im Interesse der schnelleren Wandlungszeit werden wir hier den Faktor 32 einstellen.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCL&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ADCH&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC &#039;&#039;&#039; Data Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn eine Umwandlung abgeschlossen ist, befindet sich der gemessene Wert in&lt;br /&gt;
diesen beiden Registern. Von &#039;&#039;&#039;ADCH&#039;&#039;&#039; werden nur die beiden niederwertigsten Bits verwendet. Es müssen immer beide Register ausgelesen werden, und zwar immer &#039;&#039;&#039;in der Reihenfolge: ADCL, ADCH&#039;&#039;&#039;. &lt;br /&gt;
Der effektive Messwert ergibt sich dann zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCL;       // mit uint16_t x&lt;br /&gt;
x += (ADCH&amp;lt;&amp;lt;8); // in zwei Zeilen (LSB/MSB-Reihenfolge und&lt;br /&gt;
                // C-Operatorpriorität sichergestellt)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADMUX&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Mu&#039;&#039;&#039;ltiple&#039;&#039;&#039;x&#039;&#039;&#039;er Select Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Register wird der zu messende Kanal ausgewählt. Beim 90S8535&lt;br /&gt;
kann jeder Pin von Port A als &#039;&#039;&#039;ADC&#039;&#039;&#039;-Eingang verwendet werden (=8 Kanäle).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADLAR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX4&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX3&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;REFS1...REFS0&#039;&#039;&#039; (&#039;&#039;&#039;Ref&#039;&#039;&#039;erence&#039;&#039;&#039;S&#039;&#039;&#039;election Bits)&lt;br /&gt;
:Mit diesen Bits kann die Referenzspannung eingestellt werden:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Referenzspanung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Externes AREF&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | AVCC als Referenz&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Interne 2,56 Volt&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADLAR&#039;&#039;&#039; (&#039;&#039;&#039;ADC &#039;&#039;&#039; &#039;&#039;&#039;L&#039;&#039;&#039;eft &#039;&#039;&#039;A&#039;&#039;&#039;djust &#039;&#039;&#039;R&#039;&#039;&#039;esult)&lt;br /&gt;
:Das ADLAR Bit verändert das Aussehen des Ergebnisses der AD-Wandlung. Bei einer logischen 1 wird das Ergebnis linksbündig ausgegeben, bei einer 0 rechtsündig. Eine Änderung in diesem Bit beeinflusst das Ergebnis sofort, ganz egal ob bereits eine Wandlung läuft.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX4...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 5 Bits wird der zu messende Kanal bestimmt. Wenn man einen einfachen 1-kanaligen ADC verwendet wird einfach die entsprechende Pinnummer des Ports in die Bits 0...2 eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
:Eine Empfehlung ist deswegen diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren des ADC ====&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039; ADC&#039;&#039;&#039; zu aktivieren, müssen wir das &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bit im &#039;&#039;&#039;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
Ein kleines Beispiel für den &amp;quot;single conversion&amp;quot;-Mode bei einem ATmega169 und Nutzung der internen Referenzspannung (beim &#039;169 1,1V bei anderen AVRs auch 2,56V). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler einstellen. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1023 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint16_t ReadChannel(uint8_t mux)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
&lt;br /&gt;
  ADMUX = mux;                      // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler &lt;br /&gt;
                               // setzen auf 8 (1) und ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  /* nach Aktivieren des ADC wird ein &amp;quot;Dummy-Readout&amp;quot; empfohlen, man liest&lt;br /&gt;
     also einen Wert und verwirft diesen, um den ADC &amp;quot;warmlaufen zu lassen&amp;quot; */&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
     ;     // auf Abschluss der Konvertierung warten &lt;br /&gt;
  }&lt;br /&gt;
  result = ADCW;  // ADCW muss einmal gelesen werden,&lt;br /&gt;
                  // sonst wird Ergebnis der nächsten Wandlung&lt;br /&gt;
                  // nicht übernommen.&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  result = 0; &lt;br /&gt;
  for( i=0; i&amp;lt;4; i++ )&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);            // eine Wandlung &amp;quot;single conversion&amp;quot;&lt;br /&gt;
    while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
      ;   // auf Abschluss der Konvertierung warten&lt;br /&gt;
    }&lt;br /&gt;
    result += ADCW;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Beispielaufrufe: */&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
&lt;br /&gt;
  adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -&amp;gt; Channel 0 */&lt;br /&gt;
  ...&lt;br /&gt;
  adcval = ReadChannel(2); /* MUX-Bits auf 0b0010 -&amp;gt; Channel 2 */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekennzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Das Löschen des ADIF-Flags sollte, &#039;&#039;&#039;entgegen&#039;&#039;&#039; der [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_intbits FAQ], mit&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADIF);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
erfolgen. Die Methode in der FAQ eignet sich nur für Register in denen &#039;&#039;&#039;nur&#039;&#039;&#039; Interrupt-Flags stehen.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Analog-Digital-Wandlung ohne internen ADC ===&lt;br /&gt;
&lt;br /&gt;
==== Messen eines Widerstandes ====&lt;br /&gt;
&lt;br /&gt;
Analoge Werte lassen sich ohne Analog-Digital-Wandler auch indirekt ermitteln. Im Folgenden wird die Messung des an einem Potentiometer eingestellten Widerstands anhand der Ladekurve eines Kondensators erläutert. Bei dieser Methode wird nur ein Portpin benötigt, ein Analog-Digital-Wandler oder Analog-Comparator ist nicht erforderlich. Es wird dazu ein Kondensator und der Widerstand (das Potentiometer) in Reihe zwischen Vorsorgungsspannung und Masse/GND geschaltet (sogen. RC-Netzwerk). Zusätzlich wird eine Verbindung der Leitung zwischen Kondensator und Potentiometer zu einem Portpin des Controllers hergestellt. Die folgende Abbildung verdeutlicht die erforderliche Schaltung. &lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wird der Portpin des Controllers auf Ausgang konfiguriert (im Beispiel &#039;&#039;DDRD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) und dieser Ausgang auf Logisch 1 (&amp;quot;High&amp;quot;, &#039;&#039;PORTD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) geschaltet, liegt an beiden &amp;quot;Platten&amp;quot; des Kondensators das gleiche Potential &#039;&#039;&#039;VCC&#039;&#039;&#039; an und der Kondensator somit entladen. (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so, da an beiden Seiten des Kondensators das gleiche Potential anliegt und somit eine Potentialdifferenz von 0V besteht =&amp;gt; Kondensator ist entladen).&lt;br /&gt;
&lt;br /&gt;
Nach einer gewissen Zeit ist der Kondensator entladen und der Portpin wird als Eingang konfiguriert (&#039;&#039;DDRD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2); PORTD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2)&#039;&#039;), wodurch dieser hochohmig wird. Der Status des Eingangspin (in PIND) ist Logisch 1 (High). Der Kondensator lädt sich jetzt über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), wird das Eingangssignal als LOW erkannt (Bit in PIND wird 0). Die Zeitspanne zwischen der Umschaltung von Entladung auf Aufladung und dem Wechsel des Eingangssignals von High auf Low ist ein Maß für den am Potentiometer eingestellten Widerstand. Zur Zeitmessung kann einer der im Controller vorhandenen Timer genutzt werden. Der 220 Ohm Widerstand dient dem Schutz des Controllers. Es würde sonst bei Maximaleinstellung des Potentionmeters (hier 0 Ohm) ein zu hoher Strom fließen, der die Ausgangsstufe des Controllers zerstört. &lt;br /&gt;
&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B: 0...100 % oder so) umzurechnen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Link 404 =&amp;gt; auskommentiert, mthomas 9.2.2008 &lt;br /&gt;
Ein Beispielprogramm findet sich auf [http://www.mypage.bluewin.ch/ch_schifferle/ Christian Schifferles Web-Seite] im Archiv &#039;&#039;ATMEL.ZIP&#039;&#039;, welches unter den Titel &#039;&#039;Tutorial &amp;quot;Programmieren mit C für Atmel Mikrocontroller&#039;&#039; heruntergeladen werden kann. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ADC über Komparator ====&lt;br /&gt;
&lt;br /&gt;
Es gibt einen weiteren Weg, eine analoge Spannung mit Hilfe des&lt;br /&gt;
Komparators, welcher in fast jedem AVR integriert ist, zu messen. Siehe dazu&lt;br /&gt;
auch die Application Note AVR400 von Atmel.&lt;br /&gt;
&lt;br /&gt;
Dabei wird das zu messende Signal auf den invertierenden Eingang&lt;br /&gt;
des Komparators geführt. Zusätzlich wird ein Referenzsignal an den nicht&lt;br /&gt;
invertierenden Eingang des Komparators angeschlossen. Das Referenzsignal wird&lt;br /&gt;
hier auch wieder über ein RC-Glied erzeugt, allerdings mit festen Werten für R&lt;br /&gt;
und C.&lt;br /&gt;
&lt;br /&gt;
[[Image:ADC ueber Komparator.gif]]&lt;br /&gt;
&lt;br /&gt;
Das Prinzip der Messung ist nun dem vorhergehenden recht&lt;br /&gt;
ähnlich. Durch Anlegen eines LOW-Pegels an Pin 2 wird der Kondensator zuerst&lt;br /&gt;
einmal entladen. Auch hier muss darauf geachtet werden, dass der Entladevorgang&lt;br /&gt;
genügend lang dauert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Nun wird Pin 2 auf HIGH gelegt. Der Kondensator wird geladen. Wenn die Spannung&lt;br /&gt;
über dem Kondensator die am Eingangspin anliegende Spannung erreicht hat&lt;br /&gt;
schaltet der Komparator durch. Die Zeit, welche benötigt wird, um den&lt;br /&gt;
Kondensator zu laden kann nun auch wieder als Maß für die Spannung an Pin 1&lt;br /&gt;
herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Ich habe es mir gespart, diese Schaltung auch aufzubauen und&lt;br /&gt;
zwar aus mehreren Gründen:&lt;br /&gt;
&lt;br /&gt;
# 3 Pins notwendig.&lt;br /&gt;
# Genauigkeit vergleichbar mit einfacherer Lösung.&lt;br /&gt;
# War einfach zu faul.&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung liegt allerdings darin, dass damit&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines Digital-Analog-Konverters (&#039;&#039;&#039;DAC&#039;&#039;&#039;) können wir nun auch Analogsignale ausgeben. Es gibt hier mehrere Verfahren. &amp;lt;!-- Wenn wir beim ADC die Möglichkeit haben, mit externen Komponenten zu operieren, müssen wir bei der DAC-Wandlung mit dem auskommen, was der Controller selber zu bieten hat. --mt: hmm, richtig? verstaendlich? redundant? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DAC über mehrere digitale Ausgänge ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir an den Ausgängen des Controllers ein entsprechendes&lt;br /&gt;
Widerstandsnetzwerk aufbauen haben wir die Möglichkeit, durch die Ansteuerung&lt;br /&gt;
der Ausgänge über den Widerständen einen Addierer aufzubauen, mit dessen&lt;br /&gt;
Hilfe wir eine dem Zahlenwert proportionale Spannung erzeugen können. Das&lt;br /&gt;
Schaltbild dazu kann etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
[[Image:DAC R2R.gif]]&lt;br /&gt;
&lt;br /&gt;
Es sollten selbstverständlich möglichst genaue Widerstände verwendet&lt;br /&gt;
werden, also nicht unbedingt solche mit einer Toleranz von 10% oder mehr.&lt;br /&gt;
Weiterhin empfiehlt es sich, je nach Anwendung den Ausgangsstrom über einen&lt;br /&gt;
Operationsverstärker zu verstärken.&lt;br /&gt;
&lt;br /&gt;
=== PWM (Pulsweitenmodulation) ===&lt;br /&gt;
&lt;br /&gt;
Wir kommen nun zu einem Thema, welches in aller Munde ist, aber viele&lt;br /&gt;
Anwender verstehen nicht ganz, wie &#039;&#039;&#039; PWM&#039;&#039;&#039; eigentlich funktioniert.&lt;br /&gt;
&lt;br /&gt;
Wie wir alle wissen, ist ein Mikrocontroller ein rein digitales Bauteil.&lt;br /&gt;
Definieren wir einen Pin als Ausgang, dann können wir diesen Ausgang entweder&lt;br /&gt;
auf HIGH setzen, worauf am Ausgang die Versorgungsspannung &#039;&#039;&#039; Vcc&#039;&#039;&#039; anliegt, oder aber wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt. Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen HIGH und LOW umschalten? - Richtig, wir erhalten eine Rechteckspannung, wie die folgende Abbildung zeigt:&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 1.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Rechteckspannung hat nun einen arithmetischen Mittelwert, &lt;br /&gt;
der je nach Pulsbreite kleiner oder größer ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 2.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun diese pulsierende Ausgangsspannung noch über ein RC-Glied filtern/&amp;quot;glätten&amp;quot;, dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVRs können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. &lt;br /&gt;
Dazu dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt, weshalb ein Blick ins entsprechende Datenblatt dringend angeraten wird.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren, müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| PWM-Modus des Timers ist nicht aktiv.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Timer/Counter zählt nun permanent von 0 bis zur Obergrenze&lt;br /&gt;
und wieder zurück, er wird also als sogenannter Auf-/Ab Zähler betrieben. &lt;br /&gt;
Die Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss mit den Bits &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; desselben&lt;br /&gt;
Registers die gewünschte Ausgabeart des Signals definiert werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Nicht invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Hochzählen und gesetzt beim&lt;br /&gt;
Herunterzählen.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Herunterzählen und gesetzt beim&lt;br /&gt;
Hochzählen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl, um beispielsweise den Timer/Counter als&lt;br /&gt;
nicht invertierenden 10-Bit PWM zu verwenden, heißt dann:&lt;br /&gt;
&lt;br /&gt;
alte Schreibweise (PWMxx wird nicht mehr akzeptiert)&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;PWM11)|(1&amp;lt;&amp;lt;PWM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
neue Schreibweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;WGM11)|(1&amp;lt;&amp;lt;WGM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Timer/Counter überhaupt läuft, müssen wir im Control&lt;br /&gt;
Register B &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; noch den gewünschten Takt (Vorteiler) einstellen und&lt;br /&gt;
somit auch die Frequenz des &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signals bestimmen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stop. Der Timer/Counter wird gestoppt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin 1, negative Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin 1, positive Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Also um einen Takt von CK / 1024 zu generieren, verwenden wir&lt;br /&gt;
folgenden Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1B = (1&amp;lt;&amp;lt;CS12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt muss nur noch der Vergleichswert festgelegt werden. Diesen&lt;br /&gt;
schreiben wir in das 16-Bit Timer/Counter Output Compare Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem Vergleichswert und dem generierten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal aufzeigen.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
Ach ja, fast hätte ich&#039;s vergessen. Das generierte &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal&lt;br /&gt;
wird am Output Compare Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; des Timers ausgegeben und leider können wir&lt;br /&gt;
deshalb auch beim AT90S2313 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren. Andere AVR-Typen verfügen über bis zu vier PWM-Ausgänge. Zu beachten ist außerdem, das wenn der OC Pin aktiviert ist, er nichtmehr wie üblich funktioniert und z.B. nicht einfach über PINx ausgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Ein Programm, dass an einem ATMega8 den Fast-PWM Modus verwendet, den Modus 14, könnte so aussehen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // OC1A auf Ausgang&lt;br /&gt;
  DDRB = (1 &amp;lt;&amp;lt; PB1 );&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // Timer 1 einstellen&lt;br /&gt;
  //  &lt;br /&gt;
  // Modus 14:&lt;br /&gt;
  //    Fast PWM, Top von ICR1&lt;br /&gt;
  //&lt;br /&gt;
  //     WGM13    WGM12   WGM11    WGM10&lt;br /&gt;
  //      1        1       1        0&lt;br /&gt;
  //&lt;br /&gt;
  //    Timer Vorteiler: 1&lt;br /&gt;
  //     CS12     CS11    CS10&lt;br /&gt;
  //       0        0       1&lt;br /&gt;
  //&lt;br /&gt;
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match&lt;br /&gt;
  //     COM1A1   COM1A0&lt;br /&gt;
  //       1        0&lt;br /&gt;
 &lt;br /&gt;
  TCCR1A = (1&amp;lt;&amp;lt;COM1A1) | (1&amp;lt;&amp;lt;WGM11);&lt;br /&gt;
  TCCR1B = (1&amp;lt;&amp;lt;WGM13) | (1&amp;lt;&amp;lt;WGM12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  //  den Endwert (TOP) für den Zähler setzen&lt;br /&gt;
  //  der Zähler zählt bis zu diesem Wert&lt;br /&gt;
&lt;br /&gt;
  ICR1 = 0x6FFF;&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  // der Compare Wert&lt;br /&gt;
  // Wenn der Zähler diesen Wert erreicht, wird mit&lt;br /&gt;
  // obiger Konfiguration der OC1A Ausgang abgeschaltet&lt;br /&gt;
  // Sobald der Zähler wieder bei 0 startet, wird der&lt;br /&gt;
  // Ausgang wieder auf 1 gesetzt&lt;br /&gt;
  //&lt;br /&gt;
  // Durch Verändern dieses Wertes, werden die unterschiedlichen&lt;br /&gt;
  // PWM Werte eingestellt.&lt;br /&gt;
&lt;br /&gt;
  OCR1A = 0x3FFF;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  while( 1 )&lt;br /&gt;
    ;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für Details der PWM Möglichkeiten, muß immer das jeweilge Datenblatt des Prozessors konsultiert werden, da sich die unterschiedlichen Prozessoren in ihren Möglichkeiten doch stark unterscheiden. Auch muß man aufpassen, welches zu setzende Bit in welchem Register sind. Auch hier kann es sein, dass gleichnamige Konfigurationsbits in unterschiedlichen Konfigurationsregistern (je nach konkretem Prozessortyp) sitzen.&lt;br /&gt;
&lt;br /&gt;
= LCD-Ansteuerung =&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung an der LCD-Controller-Platine ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; (ist in manchen Unterlagen auch &#039;&#039;&#039;EN&#039;&#039;&#039;  für &#039;&#039;Enable&#039;&#039; abgekürzt) benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert, ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitetet hat (diese Methode u.a. in der LCD-Library von Peter Fleury verwendet). Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt da wir wissen, welche Anschlüsse das LCDs benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin #-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin-µC&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND oder Poti (siehe oben)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD4 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD5 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD0 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD1 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD2 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD3 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man die Steuerleitungen EN und RS auf Pins an einem anderen Port legen möchte, kann man so wie in diesem [http://www.mikrocontroller.net/topic/88543#751982 Forumsbeitrag] vorgehen.&lt;br /&gt;
&lt;br /&gt;
Ok, alles ist verbunden, wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display? &lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
void lcd_data(unsigned char temp1);&lt;br /&gt;
void lcd_string(char *data);&lt;br /&gt;
void lcd_command(unsigned char temp1);&lt;br /&gt;
void lcd_enable(void);&lt;br /&gt;
void lcd_init(void);&lt;br /&gt;
void lcd_home(void);&lt;br /&gt;
void lcd_clear(void);&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y);&lt;br /&gt;
&lt;br /&gt;
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
&lt;br /&gt;
// LCD Befehle&lt;br /&gt;
&lt;br /&gt;
#define CLEAR_DISPLAY 0x01&lt;br /&gt;
#define CURSOR_HOME   0x02&lt;br /&gt;
&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
&lt;br /&gt;
#define LCD_PORT      PORTD&lt;br /&gt;
#define LCD_DDR       DDRD&lt;br /&gt;
#define LCD_RS        PD4&lt;br /&gt;
#define LCD_EN        PD5&lt;br /&gt;
// DB4 bis DB7 des LCD sind mit PD0 bis PD3 des AVR verbunden&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// sendet ein Datenbyte an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_data(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_RS);        // RS auf 1 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_command(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);        // RS auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;              // oberes Nibble holen&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;            // maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;            // unteres Nibble holen und maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// erzeugt den Enable-Puls&lt;br /&gt;
void lcd_enable(void)&lt;br /&gt;
{&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
    _delay_us(1);                   // kurze Pause&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/80900&lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Initialisierung: &lt;br /&gt;
// Muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
void lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
   LCD_DDR = LCD_DDR | 0x0F | (1&amp;lt;&amp;lt;LCD_RS) | (1&amp;lt;&amp;lt;LCD_EN);   // Port auf Ausgang schalten&lt;br /&gt;
&lt;br /&gt;
   // muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(15);&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x03;            &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);      // RS auf 0&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4 Bit Modus aktivieren &lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x02;&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4Bit / 2 Zeilen / 5x7&lt;br /&gt;
   lcd_command(0x28);&lt;br /&gt;
    &lt;br /&gt;
   // Display ein / Cursor aus / kein Blinken&lt;br /&gt;
   lcd_command(0x0C); &lt;br /&gt;
 &lt;br /&gt;
   // inkrement / kein Scrollen&lt;br /&gt;
   lcd_command(0x06);&lt;br /&gt;
&lt;br /&gt;
   lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
&lt;br /&gt;
void lcd_clear(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CLEAR_DISPLAY);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
&lt;br /&gt;
void lcd_home(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CURSOR_HOME);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)&lt;br /&gt;
&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t tmp;&lt;br /&gt;
&lt;br /&gt;
  switch (y) {&lt;br /&gt;
    case 1: tmp=0x80+0x00+x; break;    // 1. Zeile&lt;br /&gt;
    case 2: tmp=0x80+0x40+x; break;    // 2. Zeile&lt;br /&gt;
    case 3: tmp=0x80+0x10+x; break;    // 3. Zeile&lt;br /&gt;
    case 4: tmp=0x80+0x50+x; break;    // 4. Zeile&lt;br /&gt;
  }&lt;br /&gt;
  lcd_command(tmp);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_string(char *data)&lt;br /&gt;
{&lt;br /&gt;
    while(*data) {&lt;br /&gt;
        lcd_data(*data);&lt;br /&gt;
        data++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches die Funktionen benutzt, sieht zb. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen&lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    lcd_data(&#039;T&#039;);&lt;br /&gt;
    lcd_data(&#039;e&#039;);&lt;br /&gt;
    lcd_data(&#039;s&#039;);&lt;br /&gt;
    lcd_data(&#039;t&#039;);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
&lt;br /&gt;
    lcd_string(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist dabei, dass die Optimierung bei der Compilierung eingeschaltet ist, sonst stimmen die Zeiten der Funktionen _delay_us() und _delay_ms() nicht und der Code wird wesentlich länger (Siehe Dokumentation der libc im WinAVR).&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches eine Variable ausgibt, sieht zb. so aus.&lt;br /&gt;
Mittels der itoa() Funktion (itoa = &amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;nteger &amp;lt;b&amp;gt;To&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;A&amp;lt;/b&amp;gt;scii ) wird von einem Zahlenwert eine textuelle Repräsentierung ermittelt (sprich: ein String erzeugt) und dieser String mit der bereits vorhandenen Funktion lcd_string ausgegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen &lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Beispiel&lt;br /&gt;
int variable = 42;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe des Zeichens dessen ASCII-Code gleich dem Variablenwert ist&lt;br /&gt;
    // (Im Beispiel entspricht der ASCII-Code 42 dem Zeichen *)&lt;br /&gt;
    // http://www.code-knacker.de/ascii.htm&lt;br /&gt;
    lcd_data(variable);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
 &lt;br /&gt;
    // Ausgabe der Variable als Text in dezimaler Schreibweise&lt;br /&gt;
    {&lt;br /&gt;
       // ... umwandeln siehe FAQ Artikel bei www.mikrocontroller.net&lt;br /&gt;
       // WinAVR hat eine itoa()-Funktion, das erfordert obiges #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
       char Buffer[20]; // in diesem {} lokal&lt;br /&gt;
       itoa( variable, Buffer, 10 ); &lt;br /&gt;
&lt;br /&gt;
       // ... ausgeben  &lt;br /&gt;
       lcd_string( Buffer );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einrichten eines Projekts muss man zu der Datei mit dem Hauptprogramm auch die Datei lcd-routines.c in das Projekt aufnehmen. Dies geschieht beim AVR Studio unter Source Files im Fenster AVR GCC oder bei WinAVR im Makefile (z.B. durch SRC += lcd-routines.c).&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVRs sind für viele Steuerungsaufgaben zu schnell. Wenn wir beispielsweise eine LED oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die CPU-Frequenz verwenden, da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, Vorgänge in Zeitabständen durchzuführen, die geringer als die Taktfrequenz des Controllers sind. Selbstverständlich sollte die resultierende Frequenz auch noch möglichst genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist ganz einfach ein bestimmtes Register im µC, das völlig ohne Zutun des Programms, also per Hardware, hochgezählt wird. Das alleine wäre noch nicht allzu nützlich, wenn nicht dieses Hardwareregister bei bestimmten Zählerständen einen Interrupt auslösen könnte. Ein solches Ereignis ist der Overflow: Da die Bitbreite des Registers beschränkt ist, kommt es natürlich auch vor, dass der Zähler so hoch zählt, dass der nächste Zählerstand mit dieser Bitbreite nicht mehr darstellbar ist und der Zähler wieder auf 0 zurückgesetzt wird. Dieses Ereignis nennt man den Overflow und es ist möglich an dieses Ereignis einen Interrupt zu koppeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Alternativvorschlag mthomas &lt;br /&gt;
Jeder Timer verfügt über ein Zählerregister im Mikrocontroller, das automatisch und ohne Zutun des Programms von der Hardware weitergezählt wird. In einem einfachen Anwendungsfall stellt man den Timer auf eine Zählgeschwindigkeit (Frequenz) und kann dann anhand des Zählerstands ermitteln, wie viel Zeit vergangen ist. Das eigentlich Nützliche an Timern ist jedoch, dass man  bestimmte Zählerstände mit Interrupts verknüpfen kann, so dass der Controller beim Auftreten automatisch eine vom Anwender geschriebene Routine aufruft. Eines dieser möglichen Ereignis ist der Overflow ((Zähler-)Überlauf), der dann auftritt, wenn der Wert des Zählerregisters (Timer/Counter-Register) den maximal möglichen Wert überschreitet. Der Maximalwert wird durch die Bitbreite des Zählerregisters bestimmt (z.B. 255 bei 8-Bit Timern). Beim Überlauf/Overflow wird der Zähler durch die Hardware auf 0 zurückgesetzt und die Zählung beginnt von neuem. Wurde vorher der Overflow-Interrupt für den Timer aktiviert (im Timer Control Register) unterbricht der Controller automatisch die Ausführung des Hauptprogramms und verzweigt in die Interrupt-Routine des Anwenders.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auf den AT90S2313. Für andere Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer Auflösung von 65536. Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz, der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
== Der Vorteiler (Prescaler) ==&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler dient dazu, den CPU-Takt vorerst um einen einstellbaren Faktor zu reduzieren. Die so geteilte Frequenz wird den Eingängen der Timer zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024 einstellen, wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca. 4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw. Zählregister (TCNTx) mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle verfügen über mindestens einen, teilweise sogar zwei, 8-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird z.B bei AT90S2313 über folgende Register angesprochen (bei anderen Typen weitestgehend analog):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    TCCR0 |= (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst und die daran gebundene Interrupt-Routine abgearbeitet. Das TOV Flag &lt;br /&gt;
lässt sich durch das Hineinschreiben einer 1 und nicht wie erwartet einer 0 wieder zurücksetzen.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen außer den 8-Bit Timern auch 16-Bit Timer. Die 16-Bit Timer/Counter sind etwas komplexer aufgebaut als die 8-Bit Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM13&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM12 (CTC1)&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorteiler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt, und C der voreingestellte Vergleichswert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin T1, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin T1, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T1 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T1 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0, bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;TCNT1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;OCR1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;OCR1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;OCR1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;OCR1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäß folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]] [[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Ausserdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr. Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen kleiner 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4us warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.6 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t x;&lt;br /&gt;
&lt;br /&gt;
    x = 10;&lt;br /&gt;
&lt;br /&gt;
    while (x &amp;gt;= 0)&lt;br /&gt;
    {&lt;br /&gt;
      // tu was&lt;br /&gt;
&lt;br /&gt;
      x--;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog? ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Einige Controller haben einen eigenen Watchdog Oszillator, z.B. der Tiny2313 mit 128kHz. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde, beginnt der Counter von 0 an hochzuzählen. &lt;br /&gt;
Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern, müssen wir den Watchdog regelmäßig wieder neu starten bzw. rücksetzen (Watchdog Reset). &lt;br /&gt;
Dies sollte innerhalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern, muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten. Es müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. &lt;br /&gt;
Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist, wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei &#039;&#039;wdt.h&#039;&#039; (&#039;&#039;#include &amp;lt;avr/wdt.h&amp;gt;&#039;&#039;) in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch gestartet wird. &lt;br /&gt;
Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. &lt;br /&gt;
Falls ein anderer Wert gewünscht ist, so kann dies im Makfile in den Linker-Optionen eingetragen werden. &lt;br /&gt;
Dazu muss in der Zeile LDFLAGS folgende Option angefügt werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert&lt;br /&gt;
:Mögliche Timeoutwerte:&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Konstante&lt;br /&gt;
! Wert&lt;br /&gt;
! TimeOut&lt;br /&gt;
|- &lt;br /&gt;
| WDTO_15MS   &lt;br /&gt;
| 0&lt;br /&gt;
| 15 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_30MS   &lt;br /&gt;
| 1&lt;br /&gt;
| 30 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_60MS   &lt;br /&gt;
| 2&lt;br /&gt;
| 60 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_120MS   &lt;br /&gt;
| 3&lt;br /&gt;
| 120 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_250MS   &lt;br /&gt;
| 4&lt;br /&gt;
| 250 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_500MS   &lt;br /&gt;
| 5&lt;br /&gt;
| 500 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_1S   &lt;br /&gt;
| 6&lt;br /&gt;
| 1 S&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_2S   &lt;br /&gt;
| 7&lt;br /&gt;
| 2s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
== Watchdog-Anwendungshinweise ==&lt;br /&gt;
&lt;br /&gt;
Ob nun der Watchdog als Schutzfunktion überhaupt verwendet werden soll, hängt stark von der Anwendung, der genutzten Peripherie und dem Umfang und der Qualitätssicherung des Codes ab. Will man sicher gehen, dass ein Programm sich nicht in einer Endlosschleife verfängt, ist der Wachdog das geeignete Mittel dies zu verhindern. Weiterhin kann bei geschickter Programmierung der Watchdog dazu genutzt werden, bestimmte Stromsparfunktionen zu implementieren. Bei einigen neueren AVRs (z.B. dem ATTiny13) kann der Watchdog auch direkt als Timer genutzt werden, der den Controller aus einem Schlafmodus aufweckt. Auch dies kann im &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register eingestellt werden. Außerdem bietet der WD die einzige Möglichkeit einen beabsichtigten System-Reset (ein &amp;quot;richtiger Reset&amp;quot;, kein &amp;quot;jmp 0x0000&amp;quot;) ohne externe Beschaltung auszulösen, was z.B. bei der Implementierung eines Bootloaders nützlich ist. Bei bestimmten Anwendungen kann die Nutzung des WD als &amp;quot;ultimative Deadlock-Sicherung für nicht bedachte Zustände&amp;quot; natürlich immer als zusätzliche Sicherung dienen. &lt;br /&gt;
&lt;br /&gt;
Es besteht die Möglichkeit herauszufinden, ob ein Reset durch den Watchdog ausgelöst wurde (beim ATmega16 z.B. Bit WDRF in MCUCSR). Diese Information sollte auch genutzt werden, falls ein WD-Reset in der Anwendung nicht planmäßig implementiert wurde. Zum Beispiel kann man eine LED an einen freien Pin hängen, die nur bei einem Reset durch den WD aufleuchtet oder aber das &amp;quot;Ereignis WD-Reset&amp;quot; im internen EEPROM des AVR absichern, um die Information später z.B. über UART oder ein Display auszugeben (oder einfach den EEPROM-Inhalt über die ISP/JTAG-Schnittstelle auslesen).&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/77273#642501 Bug in ATtiny2313?]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt der Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft. Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe Abschnitt (TODO: verlinken) im Anhang.&lt;br /&gt;
&amp;lt;!--Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. vgl. [[AVR-GCC-Tutorial#Anhang|Anhang]])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.B. &#039;&#039;void TIMER0_OVF_vect(void)...&#039;&#039;). Der Unterschied im Vergleich zu ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben.  &amp;lt;!--Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)), als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur &lt;br /&gt;
in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechnungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechnungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0, PORTA ist danach 0b00000001. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-oder-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8 (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten &#039;&#039;Sleep-Modes&#039;&#039; (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_enable()&#039;&#039;&#039;&lt;br /&gt;
:aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_cpu()&#039;&#039;&#039;&lt;br /&gt;
: Versetzt den Controller in den Schlafmodus (sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt)&lt;br /&gt;
* &#039;&#039;&#039;sleep_disable()&#039;&#039;&#039;&lt;br /&gt;
:deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts.&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;allozieren&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich, um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory, bei AVR Controllern mit mehr als 64kB Flash auch elpm). Die Standard-Laufzeitbibliothek des avr-gcc (die avr-libc) stellt diese Funktionen nach Einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Lokale Variablen (eigentlich Konstanten) innerhalb von Funktionen können ebenfalls im Programmspeicher abgelegt werden. Dazu ist bei der Definition jedoch ein &#039;&#039;static&#039;&#039; voranzustellen, da solche &amp;quot;Variablen&amp;quot; nicht auf dem Stack bzw. (bei Optimierung) in Registern verwaltet werden können. Der Compiler &amp;quot;wirft&amp;quot; eine Warnung falls static fehlt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray2 };&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  static /*const*/ uint8_t pgmTestByteLocal PROGMEM = 0x55;&lt;br /&gt;
  static /*const*/ char pgmTestStringLocal[] PROGMEM = &amp;quot;im Flash&amp;quot;;&lt;br /&gt;
  // so nicht (static fehlt): char pgmTestStringLocalFalsch [] PROGMEM = &amp;quot;so nicht&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 // ...&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
    //...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte....&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWort);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;groß&amp;quot; (Stand avr-gcc 3.4.x). Damit ist der mögliche Speicherbereich für &amp;quot;Flash-Konstanten&amp;quot; auf 64kB begrenzt.  &amp;lt;!-- Einige avr-libc/pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher) (intern via Assembler Anweisung ELPM). Die Initialisierungswerde des Speicherinhalts jenseits der 64kB-Marke müssen dann jedoch auf anderem Weg angelegt werden (nicht PROGMEM, evtl. eigene Section und Linker-Optionen - TODO) /// alt - und nicht ganz korrekt: (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.)--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray enthält nun die Startadresse des Byte-Arrays pgmFooByteArray1&lt;br /&gt;
    // Allerdings würde ein direkter Zugriff mit diesem Pointer (z.B. temp=*ptrToArray)&lt;br /&gt;
    // &#039;&#039;&#039;nicht&#039;&#039;&#039; den Inhalt von pgmFooByteArray1[0] liefern, sondern von einer Speicherstelle&lt;br /&gt;
    // im &#039;&#039;&#039;RAM&#039;&#039;&#039;, die die gleiche Adresse hat wie pgmFooByteArray1[0]&lt;br /&gt;
    // Daher muss nun die Funktion pgm_read_byte() benutzt werden, die die in ptrToArray&lt;br /&gt;
    // enthaltene Adresse benutzt und auf das Flash zugreift.&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray enthält nun die Adresse des ersten Elements des Byte-Arrays pgmFooByteArray2&lt;br /&gt;
    // da im zweiten Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmFooByteArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Array aus Zeichenketten im Flash-Speicher ===&lt;br /&gt;
&lt;br /&gt;
Felder aus Zeichenketten im Flash-Speicher werden in zwei Schritten angelegt: Zuerst die einzelnen Elemente des Arrays und im Anschluss ein Array, in dem die Addressen der Zeichenketten abgelegt werden. Zum Auslesen wird zuerst die Adresse des i-ten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, auf das Element (die Zeichenkette) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const char str1[] PROGMEM = &amp;quot;first_A&amp;quot;;&lt;br /&gt;
const char str2[] PROGMEM = &amp;quot;second_A&amp;quot;;&lt;br /&gt;
const char str3[] PROGMEM = &amp;quot;third_A&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char *strarray1[] PROGMEM = {&lt;br /&gt;
	str1,&lt;br /&gt;
	str2,&lt;br /&gt;
	str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	int i, j, l;&lt;br /&gt;
	const char *pstrflash;&lt;br /&gt;
	char work[20], work2[20];&lt;br /&gt;
	// fuer Simulation: per volatile Optimierung verhindern, &lt;br /&gt;
	//                  da c nicht genutzt&lt;br /&gt;
	volatile char c;&lt;br /&gt;
	&lt;br /&gt;
	for ( i = 0; i &amp;lt; (sizeof(strarray1)/sizeof(strarray1[0]) ); i++ ) {&lt;br /&gt;
&lt;br /&gt;
		// setze Pointer auf die Addresse des i-ten Elements des&lt;br /&gt;
		// &amp;quot;Flash-Arrays&amp;quot; (str1, str2, ...)&lt;br /&gt;
		pstrflash = (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) );&lt;br /&gt;
		&lt;br /&gt;
		// kopiere den Inhalt der Zeichenkette von der&lt;br /&gt;
		// in pstrflash abgelegten Adresse in das work-Array&lt;br /&gt;
		// analog zu strcpy( work, strarray1[i]) wenn alles im RAM&lt;br /&gt;
		strcpy_P( work, pstrflash );&lt;br /&gt;
		// verkuerzt:&lt;br /&gt;
		strcpy_P( work2, (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) ) );&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
		// Zeichen-fuer-Zeichen&lt;br /&gt;
		l = strlen_P( pstrflash );&lt;br /&gt;
		for ( j=0; j &amp;lt; l; j++ ) {&lt;br /&gt;
			// analog zu c=strarray[i][j] wenn alles im RAM&lt;br /&gt;
			c = (char)( pgm_read_byte( pstrflash++ ) );&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	while (1) { ; }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: &amp;quot;How do I put an array of strings completely in ROM?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5)) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Vorsicht: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char textImFlashOK[] PROGMEM = &amp;quot;mit[]&amp;quot;; &lt;br /&gt;
// = Daten im &amp;quot;Flash&amp;quot;, textImFlashOK* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char* textImFlashProblem PROGMEM = &amp;quot;mit*&amp;quot;;&lt;br /&gt;
// Konflikt: Daten im BSS (lies: RAM), textImFlashFAIL* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der Initialisierungsstring von &amp;quot;textImFlashProblem&amp;quot; zu den Konstanten ans Ende des Programmcodes gelegt wird (BSS), von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte (und wird). Da der lesende Code (mittels pgm_read*) trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein weiters Problem des AVR-GCC (gesehen bei avr-gcc 3.4.1 und 3.4.2) bei der Anpassung an die Harvard-Architektur zu sein (konstanter Pointer auf variable Daten?!). Abhilfe (&amp;quot;Workaround&amp;quot;): Initialisierung bei Zeichenketten mit [] oder gleich im Code PSTR(&amp;quot;...&amp;quot;) nutzen.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden, ob es sich um eine Adresse im Flash oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind.&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die einen im Flash abgelegten String z.B. an eine UART ausgibt, würde dann so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void uart_puts_p(const char *text)&lt;br /&gt;
{&lt;br /&gt;
    char Zeichen;&lt;br /&gt;
&lt;br /&gt;
    while (Zeichen = pgm_read_byte(text))&lt;br /&gt;
    {   /* so lange, wie mittels pgm_read_byte ein Zeichen vom Flash gelesen&lt;br /&gt;
           werden konnte, welches nicht das &amp;quot;String-Endezeichen&amp;quot; darstellt */&lt;br /&gt;
&lt;br /&gt;
        /* Das gelesene Zeichen über die normalen Kanäle verschicken */&lt;br /&gt;
        uart_putc(Zeichen);&lt;br /&gt;
        text++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Flash in der Anwendung schreiben ===&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option (auch bekannt als Bootloader-Support) können Teile des Flash-Speichers auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. Bei wenigen &amp;quot;kleinen&amp;quot; AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm(Flash)- und Datenspeicher(RAM) auf. Der C-Standard und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiß&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, dass vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgegeben sind, die innerhalb weniger Taktzyklen aufeinanderfolgen müssen (&amp;quot;timed sequence&amp;quot;). Auch dies muss bei Nutzung der Funktionen aus der avr-libc/eeprom.h-Datei nicht selbst implementiert werden. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Siehe dazu folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt; // wird in aktuellen Versionen der avr-lib mit xx.h eingebunden&lt;br /&gt;
&lt;br /&gt;
// EEMEM wird bei aktuellen Versionen der avr-lib in eeprom.h definiert&lt;br /&gt;
// hier: definiere falls noch nicht bekannt (&amp;quot;alte&amp;quot; avr-libc)&lt;br /&gt;
#ifndef EEMEM&lt;br /&gt;
// alle Textstellen EEMEM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEMEM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
//...&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // &#039;Variablen&#039; eeFooByte geschrieben&lt;br /&gt;
//...&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z.B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    #define EEPROM_DEF 0xFF&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ( ( myByte = eeprom_read_byte(&amp;amp;eeFooByte) ) == EEPROM_DEF ) {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
//...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprüft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und möglicherweise noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,3);&lt;br /&gt;
&lt;br /&gt;
    /* dito etwas anschaulicher aber &amp;quot;unnütze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fließkommazahlen lassen sich recht praktisch über eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t i[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    uint8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEMEM;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
//...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklaration abgeleiteten EEPROM-Inhalt in eine Datei geschrieben werden (übliche Dateiendung: .eep, Daten im Intel Hex-Format). Damit können recht elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen (siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles). Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden (Write EEPROM), wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse (vgl. Datenblatt Abschnitt Fuse Bits) die vorherigen Daten (EESAVE programmed = 0), deren Position möglicherweise nicht mehr mit der Belegung im aktuellen Programm übereinstimmt oder den Standardwert nach &amp;quot;Chip Erase&amp;quot;: 0xFF (EESAVE unprogrammed = 1). Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Eine besondere Funktion des avr-gcc ist, dass mit entsprechenden Optionen im Makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Variable auf feste Adressen legen ===&lt;br /&gt;
&lt;br /&gt;
Gleich zu Beginn möchte ich darauf hinweisen, dass dieses Verfahren nur ein Workaround ist, mit dem man das Problem der anscheinend &amp;quot;zufälligen&amp;quot; Verteilung&lt;br /&gt;
der EEPROM-Variablen durch den Compiler etwas in den Griff bekommen kann.&lt;br /&gt;
&lt;br /&gt;
Hilfreich kann dies vor allem dann sein, wenn man z.B. über einen Kommandointerpreter (o.ä. Funktionen) direkt bestimmte EEPROM-Adressen manipulieren möchte. Auch wenn man über einen JTAG-Adapter (mk I oder mkII) den Programmablauf manipulieren möchte, indem man die EEPROM-Werte direkt ändert, kann diese Technik hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Im folgenden nun zwei Sourcelistings mit einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.h&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#inlcude &amp;lt;avr/eeprom.h&amp;gt;     // Die EEPROM-Definitionen/Macros der avr-libc einbinden&lt;br /&gt;
&lt;br /&gt;
#define   EESIZE   512      // Maximale Größe des EEPROMS&lt;br /&gt;
&lt;br /&gt;
#define   EE_DUMMY   0x000  // Dummyelement (Adresse 0 sollte nicht genutzt werden)&lt;br /&gt;
#define   EE_VALUE1  0x001  // Eine Bytevariable  &lt;br /&gt;
#define   EE_WORD1L  0x002  // Eine Wordvariable (Lowbyte)&lt;br /&gt;
#define   EE_WORD1H  0x003  // Eine Wordvariable (Highbyte)&lt;br /&gt;
#define   EE_VALUE2  0x004  // Eine weitere Bytevariable&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den Macros &#039;&#039;&#039;#define EE_VALUE1&#039;&#039;&#039; legt man den Namen und die Adresse der&lt;br /&gt;
&#039;Variablen&#039; fest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Die Adressen sollten fortlaufend, zumindest aber aufsteigend sortiert sein! Ansonsten besteht die Gefahr, daß man sehr schnell ein Durcheinander im EEPROM Speicher veranstaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Für den Compiler sind das lediglich Speicher-Adressen, über die auf das EEPROM zugegriffen wird. Der Compiler sieht nichts davon als eine echte Variable an und stößt sich daher auch nicht daran, wenn 2 Makros mit der gleichen Speicheradresse, bzw. überlappenden Speicherbereichen definiert werden. Es liegt einzig und alleine in der Hand des Programmierers, hier keinen Fehler zu machen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
   [EE_DUMMY]   = 0x00,&lt;br /&gt;
   [EE_VALUE1]  = 0x05,&lt;br /&gt;
   [EE_WORD1L]  = 0x01,   &lt;br /&gt;
   [EE_WORD1H]  = 0x00,&lt;br /&gt;
   [EE_VALUE2]  = 0xFF&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch die Verwendung eines Array, welches das gesammte EEPROM umfasst, bleibt&lt;br /&gt;
dem Compiler nicht anderes übrig, als das Array so zu platzieren, dass Element 0&lt;br /&gt;
des Arrays der Adresse 0 des EEPROMs entspricht. (&#039;&#039;Ich hoffe nur, dass die Compilerbauer daran nichts ändern!&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Wie man in dem obigen Codelisting auch sehen kann, hat das Verfahren einen kleinen Haken. Variablen die größer sind als 1 Byte, müssen etwas umständlicher&lt;br /&gt;
definiert werden. Benötigt man keine Initialisierung durch das Programm (was der Normalfall sein dürfte), dann kann man das auch so machen:&lt;br /&gt;
&lt;br /&gt;
Möchte man im EEPROM hintereinander beispielsweise Variablen, mit den Namen &#039;&#039;&#039;Wert&#039;&#039;&#039;, &#039;&#039;&#039;Anzahl&#039;&#039;&#039;, &#039;&#039;&#039;Name&#039;&#039;&#039; und &#039;&#039;&#039;Wertigkeit&#039;&#039;&#039; definieren, wobei Wert und Wertigkeit 1 Byte belegen sollen, Anzahl als 1 Wort (also 2 Bytes) und Name mit 10 Bytes reserviert werden soll, so geht auch folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes Makro definiert also seine Startadresse durch die Startadresse der unmittelbar vorhergehende &#039;Variablen&#039; plus der Anzahl der Bytes die von der vorhergehenden &#039;Variablen&#039; verbraucht werden. Dadurch ist man zumindest etwas auf der sicheren Seite, dass keine 2 &#039;Variablen&#039; im EEPROM überlappend definiert werden. Möchte man eine weitere &#039;Variable&#039; hinzufügen, so wird deren&lt;br /&gt;
Name, einfach anstelle der EE_LAST eingesetzt und eine neue Zeile für EE_LAST eingefügt, in der dann die Größe der &#039;Variablen&#039; festgelegt wird. Zb.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_PROZENT    ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( double ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EE_PROZENT legt die Startadresse für eine neue &#039;Variable&#039; des Datentyps double fest.&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf die EEPROM Werte kann dann z.B.so erfolgen:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t   temp1;&lt;br /&gt;
uint16_t  temp2;&lt;br /&gt;
&lt;br /&gt;
temp1 = eeprom_read_byte(EE_VALUE1);&lt;br /&gt;
temp2 = eeprom_read_word(EE_WORD1L);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ob die in der avr-libc vorhandenen Funktionen dafür verwendet werden können, weiß ich nicht. Aber in einigen Fällen muss man sich sowieso eigene Funktionen&lt;br /&gt;
bauen, welche die spezifischen Anforderungen (Interrupt - Atom Problem, etc.)&lt;br /&gt;
erfüllen.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist nur eine Möglichkeit, wie man dies realisieren kann. Sie bietet einem eine relativ einfache Art die EEPROM-Werte&lt;br /&gt;
auf beliebige Adressen zu legen oder Adressen zu ändern. Die Andere Möglichkeit besteht darin, die EEPROM-Werte wie folgt zu belegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
  0x00,                     //  ee_dummy&lt;br /&gt;
  0x05,                     //  ee_value1&lt;br /&gt;
  0x01,                     //  ee_word1L&lt;br /&gt;
  0x00,                     // (ee_word1H)&lt;br /&gt;
  0xFF                      //  ee_value2&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei kann man Variablen, die größer sind als 1 Byte einfacher definieren und&lt;br /&gt;
man muss nur die Highbyte- oder Lowbyte-Adresse in der &amp;quot;eeprom.h&amp;quot; definieren.&lt;br /&gt;
Allerdings muss man hier höllisch aufpassen, dass man nicht um eine oder mehrere&lt;br /&gt;
Positionen verrutscht!&lt;br /&gt;
&lt;br /&gt;
Welche der beiden Möglichkeiten man einsetzt, hängt vor allem davon ab, wieviele&lt;br /&gt;
Byte, Word und sonstige Variablen man benutzt. Gewöhnen sollte man sich an beide Varianten können ;)&lt;br /&gt;
&lt;br /&gt;
Kleine Schlussbemerkung:&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc unterstützt die Variante 1 und die Variante 2&lt;br /&gt;
* Der icc-avr Compiler unterstützt nur die Variante 2!&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Bei alten Versionen der avr-libc wurden nicht alle AVR Controller  untersützt. Z.B. bei der avr-libc Version 1.2.3 insbesondere bei AVRs &amp;quot;der neuen Generation&amp;quot; (ATmega48/88/168/169) funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register). In neueren Versionen (z.B. avr-libc 1.4.3 aus WinAVR 20050125) wurde die Zahl der unterstüzten Controller deutlich erweitert und eine Methode zur leichten Anpassung an zukünftige Controller eingeführt.&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Will oder kann man nicht auf die neue Version aktualisieren, kann der dort gezeigte Code auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;, gegebenfalls Schutz vor Unterbrechnung/Interrupt ergänzen &#039;&#039;uint8_t sreg; sreg=SREG; cli(); [EEPROM-Code] ; SREG=sreg; return;&#039;&#039;, siehe Abschnitt Interrupts). Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== EEPROM Register ===&lt;br /&gt;
Um das EEPROM anzusteuern sind drei Register von Bedeutung.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEAR Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL da in einem 8 Bit Register keine 512 Adressen adressiert werden können&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEDR Hier werden die Daten eingetragen die geschrieben werden sollen bzw. es enthält die gelesenen Daten&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EECR Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TABLE BORDER=1&amp;gt;&lt;br /&gt;
&amp;lt;TR&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;Bit&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;1&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;0&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&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;Name&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&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;TD&amp;gt;-&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERIE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEMWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERE&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;Read/Write&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;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&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;Init Value&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;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;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;TD&amp;gt;0&amp;lt;/TD&amp;gt;&lt;br /&gt;
&amp;lt;/TR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/TABLE&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bedeutung der Bits:&amp;lt;/U&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4-7 nicht belegt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 3 (EERIE) - EEPROM Ready Interrupt Enable:&amp;lt;/U&amp;gt; Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7) wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0 wird kein Interrupt ausgelöst.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 2(EEMWE) - EEPROM Master Write Enable:&amp;lt;/U&amp;gt; Dieses Bit bestimmt, daß wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE =0 ist und EEWE = 1 gesetzt wird hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst.&lt;br /&gt;
Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 1 (EEWE) - EEPROM Write Enable:&amp;lt;/U&amp;gt; Dieses Bit löst den Schreibvorgang aus wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist wird dieses Bit automatisch wieder auf 0 gesetzt und sofern EERIE gesetzt ist ein Interrupt ausgelöst. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ein Schreibvorgang sieht typischerweise wie folgt aus:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. EEPROM Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Daten übergeben an EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1 &amp;lt;BR&amp;gt;&lt;br /&gt;
5. (Optinal) Warten bis Schreibvorgang abgeschlossen ist.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 0 EERE – EEPROM Read Enable:&amp;lt;/U&amp;gt; Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit wenn das Bit EEWE=0 ist. ISt der LEsevorgang abgeschlossen wird das Bit wieder auf 0 gesetzt und das EEPROM ist für neue Lese/Schreibbefehle wieder bereit.&amp;lt;BR&amp;gt;&lt;br /&gt;
Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. Bereitschaft zum lesen prüfen (EEWE=0)&amp;lt;BR&amp;gt;&lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Lesezyklus auslösen mit EERE = 1&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Warten bis Lesevorgang abgeschlossen EERE = 0&amp;lt;BR&amp;gt;&lt;br /&gt;
5. Daten abholen aus EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bietet sich printf/sprintf an. &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t s[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( s, &amp;quot;Zählerstand: %d&amp;quot;, value );&lt;br /&gt;
    uart_puts( s );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, die Ausgabe stdout auf eigene Ausgabefunktionen umzuleiten. Dazu wird dem Ausgabemechanismus eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben (wie auch immer die Ausgabe stattfindet). Alle anderen, höheren, Funktionen greifen letztendlich auf diese eine Ausgabefunktion zurück, die die Ausgabe eines einzelnen Zeichens vornimmt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uart_init();&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
    printf( &amp;quot;Hello, world!\n&amp;quot; );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil aller printf-Varianten: Sie sind recht speicherintensiv.&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C- und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gnu-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Inline-Assembler: Die Assembleranweisungen werden direkt in den C-Code integriert. Eine Quellcode-Datei enthält somit C- und Assembleranweisungen&lt;br /&gt;
* Assembler-Dateien: Der Assemblercode befindet sich in eigenen Quellcodedateien. Diese werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung (NOP steht für No Operation). Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=Warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
   /* Verzögern der weiteren Programmausführung um&lt;br /&gt;
      genau 3 Taktzyklen */&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeintlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
&lt;br /&gt;
    /* leere Schleife - wird bei eingeschalteter Compiler-Optimierung   wegoptimiert */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* alternative Methode (keine Optimierung): */&lt;br /&gt;
    volatile uint16_t j;&lt;br /&gt;
    for (j = 0; j &amp;lt; 1000; j++)&lt;br /&gt;
      ;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützlicher &amp;quot;Assembler-Einzeiler&amp;quot; ist der Aufruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;);&#039;&#039;), da hierzu in älteren Versionen der avr-libc keine eigene Funktion existiert (in neueren Versionen &#039;&#039;sleep_cpu()&#039;&#039; aus sleep.h).&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise Delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil, dass die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static inline void delayloop16 (uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
    asm volatile (&amp;quot;cp  %A0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;cpc %B0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;breq 2f               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;1:                    \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;sbiw %0,1             \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;brne 1b               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;2:                    &amp;quot;  &lt;br /&gt;
                  : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
	          : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
    );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Anweisung wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen. Der Zeilenumbruch teilt dem Assembler mit, dass ein neuer Befehl beginnt.&lt;br /&gt;
* Als Sprung-Marken (Labels) werden Ziffern verwendet. Diese speziellen Labels sind mehrfach im Code verwendbar. Gesprungen wird jeweils zurück (b) oder vorwärts (f) zum nächsten ausffindbaren Label.&lt;br /&gt;
&lt;br /&gt;
Das Resultat zeigt ein Blick in die Assembler-Datei, die der Compiler mit der option &amp;lt;tt&amp;gt;-save-temps&amp;lt;/tt&amp;gt; nicht löscht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	cp  r24, __zero_reg__ 	 ;  count&lt;br /&gt;
	cpc r25, __zero_reg__ 	 ;  count&lt;br /&gt;
	breq 2f               &lt;br /&gt;
	1:                    &lt;br /&gt;
	sbiw r24,1             	 ;  count&lt;br /&gt;
	brne 1b               &lt;br /&gt;
	2:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc im Abschnitt [http://www.nongnu.org/avr-libc/user-manual/inline_asm.html Related Pages/Inline Asm]. &lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc Deutsche Einführung in Inline-Assembler]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
Wenn man mit dem AVR Studio arbeitet, kann alternativ auch das standardmäßig erstellte Makefile bearbeitet und folgende Zeilen eingefügt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
## Objects that must be built in order to link&lt;br /&gt;
OBJECTS = (alte Dateien...) useful.o&lt;br /&gt;
&lt;br /&gt;
## Compile&lt;br /&gt;
## Hier folgt eine Liste der gelinkten Dateien, darunter einfügen:&lt;br /&gt;
useful.o: ../useful.S&lt;br /&gt;
	$(CC) $(INCLUDES) $(ASMFLAGS) -c  $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Das war es schon. Allerdings gilt es zu beachten, dass das makefile über &amp;quot;Project -&amp;gt; Configuration options&amp;quot; selbst einzubinden ist, sonst wird es natürlich wieder überschrieben.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel eine Funktion &#039;&#039;superFunc&#039;&#039;, die alle Pins des Ports D auf &amp;quot;Ausgang&amp;quot; schaltet, eine Funktion &#039;&#039;ultraFunc&#039;&#039;, die die Ausgänge entsprechend des übergebenen Parameters schaltet, eine Funktion &#039;&#039;gigaFunc&#039;&#039;, die den Status von Port A zurückgibt und eine Funktion &#039;&#039;addFunc&#039;&#039;, die zwei Bytes zu einem 16-bit-Wort addiert. Die Zuweisungen im C-Code (PORTx = ...) verhindern, dass der Compiler die Aufrufe wegoptimiert und dienen nur zur Veranschaulichung der Parameterübergaben.&lt;br /&gt;
&lt;br /&gt;
Zuerst der Assembler-Code. Der Dateiname sei useful.S:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//; Arbeitsregister (ohne &amp;quot;r&amp;quot;) &lt;br /&gt;
workreg  = 16&lt;br /&gt;
workreg2 = 17&lt;br /&gt;
&lt;br /&gt;
//; Konstante:&lt;br /&gt;
ALLOUT = 0xff&lt;br /&gt;
&lt;br /&gt;
//; ** Setze alle Pins von PortD auf Ausgang **&lt;br /&gt;
//; keine Parameter, keine Rückgabe&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg  // beachte: _SFR_IO_ADDR()&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Setze PORTD auf übergebenen Wert **&lt;br /&gt;
//; Parameter in r24 (LSB immer bei &amp;quot;graden&amp;quot; Nummern)&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zustand von PINA zurückgeben **&lt;br /&gt;
//; Rückgabewerte in r24:r25 (LSB:MSB), hier nur LSB genutzt&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zwei Bytes addieren und 16-bit-Wort zurückgeben **&lt;br /&gt;
//; Parameter in r24 (Summand1) und r22 (Summand2) -&lt;br /&gt;
//;  Parameter sind Word-&amp;quot;aligned&amp;quot; d.h. LSB immer auf &amp;quot;graden&amp;quot;&lt;br /&gt;
//;  Registernummern. Bei 8-Bit und 16-Bit Paramtern somit &lt;br /&gt;
//;  beginnend bei r24 dann r22 dann r20 etc.&lt;br /&gt;
//; Rückgabewert in r24:r25&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   push workreg2&lt;br /&gt;
   clr workreg2&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
   pop workreg2&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
//; oh je - sorry - Mein AVR-Assembler ist eingerostet, hoffe das stimmt so...&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Makefile ist der Name der Assembler-Quellcodedatei einzutragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
ASRC = useful.S&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Aufruf erfolgt dann im C-Code so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
extern void superFunc(void);&lt;br /&gt;
extern void ultraFunc(uint8_t setVal);&lt;br /&gt;
extern uint8_t gigaFunc(void);&lt;br /&gt;
extern uint16_t addFunc(uint8_t w1, uint8_t w2);&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
[...]&lt;br /&gt;
  superFunc();&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
&lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
[...]&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird wieder in der lss-Datei ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
   superFunc();&lt;br /&gt;
 148:	0e 94 f6 00 	call	0x1ec&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
 14c:	85 e5       	ldi	r24, 0x55	; 85&lt;br /&gt;
 14e:	0e 94 fb 00 	call	0x1f6&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
 152:	0e 94 fd 00 	call	0x1fa&lt;br /&gt;
 156:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
  &lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
 158:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 15a:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 15c:	0e 94 ff 00 	call	0x1fe&lt;br /&gt;
 160:	8b bb       	out	0x1b, r24	; 27&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
 162:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 164:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 166:	0e 94 fc 00 	call	0x1f8&lt;br /&gt;
 16a:	89 2f       	mov	r24, r25&lt;br /&gt;
 16c:	99 27       	eor	r25, r25&lt;br /&gt;
 16e:	88 bb       	out	0x18, r24	; 24&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
000001ec &amp;lt;superFunc&amp;gt;:&lt;br /&gt;
// setze alle Pins von PortD auf Ausgang&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1ec:	0f 93       	push	r16&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
 1ee:	0f ef       	ldi	r16, 0xFF	; 255&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg&lt;br /&gt;
 1f0:	01 bb       	out	0x11, r16	; 17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 1f2:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 1f4:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001f6 &amp;lt;ultraFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// setze PORTD auf übergebenen Wert&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
 1f6:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
   ret&lt;br /&gt;
 1f8:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fa &amp;lt;gigaFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Zustand von PINA zurückgeben&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
 1fa:	89 b3       	in	r24, 0x19	; 25&lt;br /&gt;
   ret&lt;br /&gt;
 1fc:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fe &amp;lt;addFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// zwei Bytes addieren und 16-bit-Wort zurückgeben&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1fe:	0f 93       	push	r16&lt;br /&gt;
   push workreg2&lt;br /&gt;
 200:	1f 93       	push	r17&lt;br /&gt;
   clr workreg2&lt;br /&gt;
 202:	11 27       	eor	r17, r17&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
 204:	06 2f       	mov	r16, r22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
 206:	08 0f       	add	r16, r24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
 208:	11 1d       	adc	r17, r1&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
 20a:	c8 01       	movw	r24, r16&lt;br /&gt;
   pop workreg2&lt;br /&gt;
 20c:	1f 91       	pop	r17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 20e:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 210:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zuweisung von Registern zu Parameternummer und die Register für die Rückgabewerte sind in den &amp;quot;Register Usage Guidelines&amp;quot; der avr-libc-Dokumentation erläutert.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/assembler.html avr-libc-Dokumentation: Related Pages/avr-libc and assembler programs]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage avr-libc-Dokumentation: Related Pages/FAQ/&amp;quot;What registers are used by the C compiler?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
== Globale Variablen für Datenaustausch ==&lt;br /&gt;
&lt;br /&gt;
Oftmals kommt man um globale Variablen nicht herum, z.B. um den Datenaustausch zwischen Hauptprogramm und Interrupt-Routinen zu realisieren. &lt;br /&gt;
Hierzu muss man im Assembler wissen, wo genau die Variable vom C-Compiler abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
Hierzu muss die Variable, hier &amp;quot;zaehler&amp;quot; genannt, zuerst im C-Code als Global definiert werden, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
int16_t main (void)&lt;br /&gt;
{&lt;br /&gt;
    // irgendein Code, in dem zaehler benutzt werden kann&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im foldenden Assembler-Beispiel wird der Externe Interrupt0  verwendet, um den Zähler hochzuzählen. Es fehlen die Initialisierungen des Interrupts und die Interrupt-Freigabe, so richtig sinnvoll ist der Code auch nicht, aber er zeigt (hoffentlich) wie es geht.&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit Interrupt-Vektoren gilt beim GCC-Assembler das Gleiche, wie bei C: Man muss die exakte Schreibweise beachten, ansonsten wird nicht der Interrupt-Vektor angelegt, sondern eine neue Funktion - und man wundert sich, dass nichts funktionert (vgl. das AVR-GCC-Handbuch).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
.extern zaehler&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      //; wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     //; Status-Register (SREG) sichern!&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     lds temp,zaehler               //; Wert aus dem Speicher lesen&lt;br /&gt;
     inc temp                       //; bearbeiten&lt;br /&gt;
     sts zaehler,temp               //; und wieder zurückschreiben&lt;br /&gt;
&lt;br /&gt;
     pop temp                       //; die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen im Assemblerfile anlegen ===&lt;br /&gt;
&lt;br /&gt;
Alternativ können Variablen aber auch im Assemblerfile angelegt werden. Dadurch kann auf eine .c-Datei verzichtet werden. Für das obige Beispiel könnte der Quelltext dann die Dateien zaehl_asm.S und zaehl_asm.h abgelegt werden, so dass nur noch zaehl_asm.S mit kompiliert werden müsste.&lt;br /&gt;
&lt;br /&gt;
Anstatt im Assemblerfile über das Schlüsselwort &#039;&#039;.extern &#039;&#039; auf eine vorhandene Variable zu verweisen, wird dazu mit dem Schlüsselwort &#039;&#039;.comm&#039;&#039; die benötigte Anzahl von Bytes für eine Variable reserviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.S&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
//; 1 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 1&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Headerdatei wird dann auf die Variable nur noch verwiesen (Schlüsselwort &#039;&#039;extern&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef ZAEHL_ASM_H&lt;br /&gt;
#define ZAEHL_ASM_H&lt;br /&gt;
&lt;br /&gt;
extern volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu globalen Variablen in C werden so angelegte Variablen nicht automatisch mit dem Wert 0 initialisiert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer als 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Variablen, die größer als &#039;&#039;&#039;ein&#039;&#039;&#039; Byte sind, können in Assembler auf ähnliche Art angesprochen werden. Hierzu müssen nur genug Bytes angefordert werden, um die Variable aufzunehmen. Soll z.B. für den Zähler eine Variable vom Typ &#039;&#039;unsigned long&#039;&#039;, also &#039;&#039;uint32_t&#039;&#039; verwendet werden, so müssen 4 Bytes reserviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die dazugehörige Deklaration im Headerfile wäre dann:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
extern volatile uint32_t zaehler;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen, die größer als ein Byte sind, werden die Werte beginnend mit dem niederwertigsten Byte im RAM abgelegt. Das folgende Codeschnippsel zeigt, wie unter Assembler auf die einzelnen Bytes zugegriffen werden kann. Dazu wird im Interrupt nun ein 32-Bit Zähler erhöht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      // wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     // Status-Register (SREG) sichern !&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     // 32-Bit-Zähler incrementieren&lt;br /&gt;
     lds temp, (zaehler + 0)        // 0. Byte (niederwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 0), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
     lds temp, (zaehler + 1)        // 1. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 1), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 2)        // 2. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 2), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 3)        // 3. Byte (höchstwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 3), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
RAUS:&lt;br /&gt;
     pop temp                       // die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TODO:&#039;&#039;&#039; 16-Bit / 32-Bit Variablen, Zugriff auf Arrays (Strings)&lt;br /&gt;
&lt;br /&gt;
= Anhang =&lt;br /&gt;
&lt;br /&gt;
== Besonderheiten bei der Anpassung bestehenden Quellcodes ==&lt;br /&gt;
&lt;br /&gt;
Einige Funktionen, die in frühren Versionen der avr-libc vorhanden waren, werden inzwischen als veraltet angesehen. Sie sind nicht mehr vorhanden oder als &#039;&#039;deprecated&#039;&#039; (missbilligt) ausgewiesen und Definitionen in &amp;lt;compat/deprecated.h&amp;gt; verschoben. Es empfiehlt sich, vorhandenen Code zu portieren und die alten Funktionen nicht mehr zu nutzen, auch wenn diese noch zur Verfügung stehen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zur Deklaration von Interrupt-Routinen ===&lt;br /&gt;
&lt;br /&gt;
Die Funktionen (eigentlich Makros) &#039;&#039;SIGNAL&#039;&#039; und &#039;&#039;INTERRUPT&#039;&#039; zur Deklaration von Interruptroutinen sollten nicht mehr genutzt werden. &lt;br /&gt;
&lt;br /&gt;
In aktuellen Versionen der avr-libc (z.B. avr-libc 1.4.3 aus WinAVR 20060125) werden Interruptroutinen, die &#039;&#039;&#039;nicht&#039;&#039;&#039; durch andere Interrupts &#039;&#039;&#039;unterbrechbar&#039;&#039;&#039; sind, mit ISR deklariert (siehe Abschnitt im Hauptteil). Auch die Benennung wurden vereinheitlicht und an die üblichen Bezeichnungen in den AVR Datenblättern angepasst. In der Dokumentation der avr-libc sind alte und neue Bezeichnungen in der Tabelle gegenübergestellt. Die erforderlichen Schritte zur Portierung:&lt;br /&gt;
&lt;br /&gt;
* #include von avr/signal.h entfernen&lt;br /&gt;
* SIGNAL duch ISR ersetzen&lt;br /&gt;
* Name des Interrupt-Vektors anpassen (SIG_* durch entsprechendes *_vect)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für die Anpassung zuerst ein &amp;quot;alter&amp;quot; Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer2 Output Compare bei einem ATmega8 */&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE2)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt wird der Vektor mit TIMER2 COMP bezeichnet. Die Bezeichnung in der avr-libc entspricht dem Namen im Datenblatt, Leerzeichen werden durch Unterstriche (_) ersetzt und ein _vect angehängt. &lt;br /&gt;
&lt;br /&gt;
Der neue Code sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt; &lt;br /&gt;
/* signal.h entfällt */&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER2_COMP_vect)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Unklarheiten bezüglich der neuen Vektorlabels hilft (noch) ein Blick in die Headerdatei des entsprechenden Controllers. Für das vorherige Beispiel also der Blick in die Datei iom8.h für den ATmega8, dort findet man die veraltete Bezeichnung unterhalb der aktuellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect		_VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2		_VECTOR(3)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Nachfolgendes mit avr-libc 1.4.5 (in WinAVR 1/2007 behoben - noch eine Weile auskommentiert lassen und dann löschen.&lt;br /&gt;
Konnte in alten Versionen signal.h ohne interrupt.h eingebunden werden, erhält man bei Verwendung der avr-libc Version 1.4.3 (WinAVR 2/2005) beim Compilieren eine Fehlermeldung, da mit signal.h nicht die erforderlichen Definitionen eingebunden werden. Der Lösungsvorschlag in signal.h auch interrupt.h einzubinden, wurde von den avr-libc-Enwicklern akzeptiert und das Problem ist  im Quellcode (CVS) bereits behoben. Es ist aber noch keine avr-libc-&amp;quot;Release&amp;quot; bzw. noch kein WinAVR mit dieser avr-libc-Korrektur verfügbar (Stand 5.2.2006). Will oder kann man den Quellcode nicht aktualisieren, gibt es folgende Alternativen:&lt;br /&gt;
* in Quellcodedateien, in denen nur avr/signal.h eingebunden wird, interrupt.h einbinden (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;). signal.h weiterhin einbinden, falls Kompatibiltät mit alten Versionen gewünscht ist.&lt;br /&gt;
* in der Datei signal.h (bein WinAVR in c:/WinAVR/avr/include/avr/signal.h) ein (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;) ergänzen --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;unterbrechbare&#039;&#039;&#039; Interruptroutinen, die mittels &#039;&#039;INTERRUPT&#039;&#039; deklariert sind, gibt es keinen direkten Ersatz in Form eines Makros. Solche Routinen sind laut Dokumentation der avr-libc in folgender Form zu deklarieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* ** alt ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
INTERRUPT(SIG_OVERFLOW0)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ** neu: ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void TIMER0_OVF_vect(void) __attribute__((interrupt));&lt;br /&gt;
void TIMER0_OVF_vect(void) &lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von &#039;&#039;INTERRUPT&#039;&#039; die Header-Datei &#039;&#039;compat/deprecated.h&#039;&#039; einzubinden. Man sollte bei dieser Gelegenheit jedoch nochmals überprüfen, ob die Funktionalität von &#039;&#039;INTERRUPT&#039;&#039; tatsächlich gewollt ist. In vielen Fällen wurde &#039;&#039;INTERRUPT&#039;&#039; dort genutzt, wo eigentlich &#039;&#039;SIGNAL&#039;&#039; (nunmehr &#039;&#039;ISR&#039;&#039;) hätte genutzt werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;inp&#039;&#039; und &#039;&#039;outp&#039;&#039; zum Einlesen bzw. Schreiben von Registern sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
unsigned char i, j;&lt;br /&gt;
&lt;br /&gt;
// alt:&lt;br /&gt;
  i = inp(PINA);&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  outp(PORTB, j);&lt;br /&gt;
&lt;br /&gt;
// neu (nicht mehr wirklich neu...):&lt;br /&gt;
  i = PINA&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  PORTB = j;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von inp und outp die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Zugriff auf Bits in Registern ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;cbi&#039;&#039; und &#039;&#039;sbi&#039;&#039; zum Löschen und Setzen von Bits sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg. Die Bezeichnung ist ohnehin irreführend da die Funktionen nur für Register mit Adressen im unteren Speicherbereich tatsächlich in die Assembleranweisungen cbi und sbi übersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// alt:&lt;br /&gt;
  sbi(PORTB, PB2);&lt;br /&gt;
  cbi(PORTC, PC1);&lt;br /&gt;
&lt;br /&gt;
// neu (auch nicht mehr wirklich neu...):&lt;br /&gt;
  PORTB |=  (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
  PORTC &amp;amp;= ~(1&amp;lt;&amp;lt;PC1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von sbi und cbi die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden. Wer unbedingt will, kann sich natürlich eigene Makros mit aussagekräftigeren Namen definieren. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &amp;amp;= ~(1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Selbstdefinierte (nicht-standardisierte) ganzzahlige Datentypen ===&lt;br /&gt;
&lt;br /&gt;
Bei den im Folgenden genannten Typdefinitionen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich, bei der Überarbeitung von altem Code die im Abschnitt &#039;&#039;standardisierten ganzzahligen Datentypen&#039;&#039; beschriebenen Datentypen zu nutzen (stdint.h) und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef unsigned char      BYTE;       // besser: uint8_t  aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned short     WORD;       // besser: uint16_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long      DWORD;      // besser: uint32_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long long QWORD;      // besser: uint64_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; BYTE : Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 255. &lt;br /&gt;
&lt;br /&gt;
; WORD : Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 65535. &lt;br /&gt;
&lt;br /&gt;
; DWORD : Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
; QWORD : Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Funktionen im Makefile ==&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken (Libraries/.a-Dateien) hinzufügen ===&lt;br /&gt;
&lt;br /&gt;
Um Funktionen aus Bibliotheken (&amp;quot;echte&amp;quot; Libraries, *.a-Dateien) zu nutzen, sind dem Linker die Namen der Bibliotheken als Parameter zu übergeben. Dazu ist die Option -l (kleines L) vorgesehen, an die der Name der Library angehängt wird. &lt;br /&gt;
&lt;br /&gt;
Dabei ist zu beachten, dass der Name der Library und der Dateiname der Library nicht identisch sind. Der hinter -l angegebene Name entspricht dem Dateinamen der Library ohne die Zeichenfolge &#039;&#039;lib&#039;&#039; am Anfang des Dateinamens und ohne die Endung &#039;&#039;.a&#039;&#039;. Sollen z.B. Funktionen aus einer Library mit dem Dateinamen &#039;&#039;libefsl.a&#039;&#039; eingebunden (gelinkt) werden, lautet der entsprechende Parameter -lefsl (vergl. auch -lm zum Anbinden von libm.a). &lt;br /&gt;
&lt;br /&gt;
In Makefiles wird traditonell eine make-Variable LDLIBS genutzt, in die &amp;quot;l-Parameter&amp;quot; abgelegt werden. Die WinAVR-makefile-Vorlage enthält diese Variable zwar nicht, dies stellt jedoch keine Einschränkung dar, da alle in der make-Variable LDFLAGS abgelegten Parameter an den Linker weitergereicht werden. &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Einbinden von Funktionen aus einer Library efsl (Dateiname libefsl.a)&lt;br /&gt;
LDFLAGS += -lefsl&lt;br /&gt;
# Einbinden von Funktionen aus einer Library xyz (Dateiname libxyz.a)&lt;br /&gt;
LDFLAGS += -lxyz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Liegen die Library-Dateien nicht im Standard Library-Suchpfad, sind die Pfade mittels Parameter &#039;&#039;-L&#039;&#039; ebenfalls anzugeben. (Der vordefinierte Suchpfad kann mittels &#039;&#039;avr-gcc --print-search-dirs&#039;&#039; angezeigt werden.)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Projekt (&amp;quot;superapp2&amp;quot;), in dem der Quellcode von zwei Libraries (efsl und xyz) und der Quellcode der eigentlichen Anwendung in verschiedenen Verzeichnissen mit der folgenden &amp;quot;Baumstruktur&amp;quot; abgelegt sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
superapp2&lt;br /&gt;
|&lt;br /&gt;
+----- efslsource (darin libefsl.a)&lt;br /&gt;
|&lt;br /&gt;
+----- xyzsource (darin libxyz.a)&lt;br /&gt;
|&lt;br /&gt;
+----- firmware (darin Anwendungs-Quellcode und Makefile)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt, dass im Makefile die Verzeichnis efslsource und xyzsource in den Library-Suchpfad aufzunehmen sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
LDFLAGS += -L../efslsource/ -L../xyzsource/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fuse-Bits ===&lt;br /&gt;
&lt;br /&gt;
Zur Berechnung der Fuse-Bits bietet sich neben dem Studium des Datenblattes auch der [http://palmavr.sourceforge.net/cgi-bin/fc.cgi AVR Fuse Calculator] an. Gewarnt werden muss vor der Benutzung von PonyProg, weil dort durch die negierte Darstellung gern Fehler gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Soll die Programmierung von Fuse- und Lockbits automatisiert werden, kann man dies ebenfalls durch Einträge im Makefile vornehmen, die beim Aufruf von &amp;quot;make program&amp;quot; an die genutzte Programmiersoftware übergeben werden. In der makefile-Vorlage von WinAVR (und mfile) gibt es dafuer jedoch keine &amp;quot;Ausfüllhilfe&amp;quot; (Stand 9/2006). Die folgenden Ausführungen gelten für die Programmiersoftware [[AVRDUDE]] (Standard in der WinAVR-Vorlage), können jedoch sinngemäß auf andere Programmiersoftware übertragen werden, die die Angabe der Fuse- und Lockbits-Einstellungen per Kommandozeilenparameter unterstützt (z.B. stk500.exe). Im einfachsten Fall ergänzt man im Makefile einige Variablen, deren Werte natürlich vom verwendeten Controller und den gewünschten Einstellungen abhängen (vgl. Datenblatt Fuse-/Lockbits):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#---------------- Programming Options (avrdude) ----------------&lt;br /&gt;
&lt;br /&gt;
#...&lt;br /&gt;
#Beispiel! f. ATmega16 - nicht einfach uebernehmen! Zahlenwerte anhand&lt;br /&gt;
#--------- des Datenblatts nachvollziehen und gegebenenfalls aendern.&lt;br /&gt;
#&lt;br /&gt;
AVRDUDE_WRITE_LFUSE = -U lfuse:w:0xff:m&lt;br /&gt;
AVRDUDE_WRITE_HFUSE = -U hfuse:w:0xd8:m&lt;br /&gt;
AVRDUDE_WRITE_LOCK  = -U lock:w:0x2f:m&lt;br /&gt;
#...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit diese Variablen auch genutzt werden, ist der Aufruf von avrdude im Makefile entsprechend zu ergänzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Program the device.  &lt;br /&gt;
program: $(TARGET).hex $(TARGET).eep&lt;br /&gt;
# ohne Fuse-/Lock-Einstellungen (nach WinAVR Vorlage Stand 4/2006)&lt;br /&gt;
#	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
#        $(AVRDUDE_WRITE_EEPROM)&lt;br /&gt;
# mit Fuse-/Lock-Einstellungen&lt;br /&gt;
        $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_LFUSE) \&lt;br /&gt;
        $(AVRDUDE_WRITE_HFUSE) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
        $(AVRDUDE_WRITE_EEPROM) $(AVRDUDE_WRITE_LOCK)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weiter Möglichkeit besteht darin, die Fuse- und Lockbit-Einstellungen vom Preprozessor/Compiler generieren zu lassen. Die Fuse-Bits werden dann bei Verwendung von AVRDUDE in eigene Hex-Files geschrieben. Hierzu kann man z.B. folgendes Konstrukt verwenden:&lt;br /&gt;
&lt;br /&gt;
In eine der C-Sourcen wird eine Variable je Fuse-Byte vom Typ &#039;&#039;unsigned char&#039;&#039; deklariert und in eine extra Section gepackt. Dies kann entweder in einem vorhandenen File passieren oder in ein neues (z.B. fuses.c) geschrieben werden. Das File muss im Makefile aber auf jeden Fall mit kompiliert und gelinkt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// tiny 2313 fuses low byte&lt;br /&gt;
#define CKDIV8  7&lt;br /&gt;
#define CKOUT   6&lt;br /&gt;
#define SUT1    5&lt;br /&gt;
#define SUT0    4&lt;br /&gt;
#define CKSEL3  3&lt;br /&gt;
#define CKSEL2  2&lt;br /&gt;
#define CKSEL1  1&lt;br /&gt;
#define CKSEL0  0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses high byte&lt;br /&gt;
#define DWEN       7&lt;br /&gt;
#define EESAVE     6&lt;br /&gt;
#define SPIEN      5&lt;br /&gt;
#define WDTON      4&lt;br /&gt;
#define BODLEVEL2  3&lt;br /&gt;
#define BODLEVEL1  2&lt;br /&gt;
#define BODLEVEL0  1&lt;br /&gt;
#define RSTDISBL   0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses extended byte&lt;br /&gt;
#define SELFPRGEN  0&lt;br /&gt;
&lt;br /&gt;
#define LFUSE         __attribute__ ((section (&amp;quot;lfuses&amp;quot;)))&lt;br /&gt;
#define HFUSE         __attribute__ ((section (&amp;quot;hfuses&amp;quot;)))&lt;br /&gt;
#define EFUSE         __attribute__ ((section (&amp;quot;efuses&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// select ext crystal 3-8Mhz&lt;br /&gt;
unsigned char lfuse LFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;CKDIV8) | (1&amp;lt;&amp;lt;CKOUT) | (1&amp;lt;&amp;lt;CKSEL3) | (1&amp;lt;&amp;lt;CKSEL2) | &lt;br /&gt;
      (0&amp;lt;&amp;lt;CKSEL1) | (1&amp;lt;&amp;lt;CKSEL0) | (0&amp;lt;&amp;lt;SUT1) | (1&amp;lt;&amp;lt;SUT0) );&lt;br /&gt;
unsigned char hfuse HFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;DWEN) | (1&amp;lt;&amp;lt;EESAVE) | (0&amp;lt;&amp;lt;SPIEN) | (1&amp;lt;&amp;lt;WDTON) | &lt;br /&gt;
      (1&amp;lt;&amp;lt;BODLEVEL2) | (1&amp;lt;&amp;lt;BODLEVEL1) | (0&amp;lt;&amp;lt;BODLEVEL0) | (1&amp;lt;&amp;lt;RSTDISBL) );&lt;br /&gt;
unsigned char efuse EFUSE =&lt;br /&gt;
    ((0&amp;lt;&amp;lt;SELFPRGEN));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG: Die Bitpositionen wurden nicht vollständig getestet!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Eine &amp;quot;1&amp;quot; bedeutet hier, dass das Fuse-Bit &#039;&#039;nicht&#039;&#039; programmiert wird - die Funktion also i.A. nicht aktiviert ist. Eine &amp;quot;0&amp;quot; hingegen aktiviert die meisten Funktionen. Dies ist wie im Datenblatt (1 = unprogrammed, 0 = programmed).&lt;br /&gt;
&lt;br /&gt;
Das Makefile muss nun noch um folgende Targets erweitert werden (mit Tabulator einrücken - nicht mit Leerzeichen):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
lfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j lfuses --change-section-address lfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-lfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-lfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U lfuse:w:$(TARGET)-lfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
hfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j hfuses --change-section-address hfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-hfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-hfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U hfuse:w:$(TARGET)-hfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
efuses: build&lt;br /&gt;
        -$(OBJCOPY) -j efuses --change-section-address efuses=0 \&lt;br /&gt;
         -O ihex $(TARGET).elf $(TARGET)-efuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-efuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U efuse:w:$(TARGET)-efuse.hex;&lt;br /&gt;
        fi;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Target &amp;quot;clean&amp;quot; muss noch um die Zeilen&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
        $(REMOVE) $(TARGET)-lfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-hfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-efuse.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
erweitert werden, wenn auch die Fuse-Dateien gelöscht werden sollen.&lt;br /&gt;
&lt;br /&gt;
Um nun die Fusebits des angeschlossenen Controllers zu programmieren muss lediglichein &amp;quot;make lfuses&amp;quot;, &amp;quot;make hfuses&amp;quot; oder &amp;quot;make efuses&amp;quot; gestartet werden.&lt;br /&gt;
Bei den Fuse-Bits ist besondere Vorsicht geboten, da diese das Programmieren des Controllers unmöglich machen können. Also erst programmieren, wenn man einen HV-Programmierer hat oder ein paar Reserve-AVRs zur Hand ;-)&lt;br /&gt;
&lt;br /&gt;
Um weiterhin den &amp;quot;normalen&amp;quot; Flash beschreiben zu können, ist es wichtig, für das Target &amp;quot;*.hex&amp;quot; im Makefile nicht nur &amp;quot;-R .eeprom&amp;quot; als Parameter zu übergeben sondern zusätzlich noch &amp;quot;-R lfuses -R efuses -R hfuses&amp;quot;. Sonst bekommt AVRDUDE Probleme diese Sections in den Flash (wo sie ja nicht hingehören) zu schreiben.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[AVR_Fuses#Vergleich_der_Fuses_bei_verschiedenen_Programmen|Vergleich der Fuses bei verschiedenen Programmen]]&lt;br /&gt;
&lt;br /&gt;
== Externe Referenzspannung des internen Analog-Digital-Wandlers ==&lt;br /&gt;
&lt;br /&gt;
Die minimale (externe) Referenzspannung des ADC darf nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. z.B. beim ATMEGA8 darf sie laut Datenblatt (S.245, Tabelle 103, Zeile &amp;quot;VREF&amp;quot;) 2,0V nicht unterschreiten. HINWEIS: diese Information findet sich erst in der letzten Revision (Rev. 2486O-10/04) des Datenblatts.&lt;br /&gt;
&lt;br /&gt;
Meiner &amp;lt;!-- Wer? - es gibt inzwischen x Leute die mehr oder weniger viel in diesem Artikel geschrieben haben --&amp;gt; eigenen Erfahrung nach kann man aber (auf eigene Gefahr und natürlich nicht für Seriengeräte) durchaus noch ein klein wenig weiter heruntergehen, bei dem von mir unter die Lupe genommenen ATMEGA8L (also die Low-Voltage-Variante) funktioniert der ADC bei 5V Betriebsspannung mit bis zu VREF=1,15V hinunter korrekt, ab 1,1V und darunter digitalisiert er jedoch nur noch Blödsinn). Ich würde sicherheitshalber nicht unter 1,5V gehen und bei niedrigeren Betriebsspannungen mag sich die Untergrenze für VREF am Pin AREF ggf. nach oben&#039;&#039;&#039;(!)&#039;&#039;&#039; verschieben.&lt;br /&gt;
&lt;br /&gt;
In der letzten Revision des Datenblatts ist außerdem korrigiert, dass ADC4 und ADC5 sehr wohl 10 Bit Genauigkeit bieten (und nicht bloß 8 Bit, wie in älteren Revisionen irrtümlich angegeben.)&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* stdio.h, malloc() &lt;br /&gt;
* Code-Optimierungen (&amp;quot;tricks&amp;quot;), siehe auch Application Note [http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf AVR035: Efficient C Coding for AVR]&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
* SPI siehe [http://www.uni-koblenz.de/~physik/informatik/MCU/SPI.pdf SPI Bus mit Atmel AVR]&lt;br /&gt;
* I²C / TWI Bus [http://www.roboternetz.de/wissen/index.php/TWI]&lt;br /&gt;
* Bootloader (bez. auf boot.h)&lt;br /&gt;
* CAN-Bus&lt;br /&gt;
* Einsatz von einfachen Betriebssystemen auf dem AVR&lt;br /&gt;
* Übersicht zu den C bzw. GCC-predefined Makros (__DATE__, __TIME__,...)&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
* ADC ; &lt;br /&gt;
* Timer&lt;br /&gt;
* USB ; Steuerung mit USB&lt;br /&gt;
* Multiplexen Siebensegment&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28248</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28248"/>
		<updated>2008-05-27T16:07:21Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Benötigte Werkzeuge */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[AVR-GCC]] erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Progammiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong]. Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern, weder in Assembler noch in einer anderen Sprache. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR Controllern finden. Bei WinAVR gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden stetig weiterentwickelt. Erläuterungen und Beispiele beziehen sich auf den C-Compiler avr-gcc ab Version 3.4 und die avr-libc ab Version 1.4.3. Die Unterschiede zu älteren Versionen werden im Haupttext und Anhang zwar erläutert, Anfängern sei jedoch empfohlen, die aktuellen Versionen zu nutzen (für MS-Windows: aktuelle Version des [[WinAVR]]-Pakets). &lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in PDF-Form erhältlich bei:&lt;br /&gt;
http://www.siwawi.arubi.uni-kl.de/avr_projects/AVR-GCC-Tutorial_-_www_mikrocontroller_net.pdf&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels avr-gcc/avr-libc zu erstellen und zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR Controllers, der vom avr-gcc Compiler unterstützt wird (alle ATmegas und die meisten AT90, siehe Dokumentation der avr-libc für unterstützte Typen). Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man [[AVR#Starterkits|hier]].&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc Compiler und die avr-libc. Kostenlos erhältlich für nahezu alle Plattformen und Betriebssysteme. Für MS-Windows im Paket [[WinAVR]]; für Unix/Linux siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]]).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
*[[AVR Checkliste]]&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/2 GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  von den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
* Die Beispieleprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweise ist aber auch auf C-Programme übertragbar.&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien um das Problem nachzuvollziehen sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z.B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu zwei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer Integrierten Entwicklungsumgebung (IDE), bei der alle Einstellungen z.B. in Dialogboxen durchgeführt werden können. Unter Anderen kann AVRStudio ab Version 4.12 (kostenlos bei atmel.com) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs für den avr-gcc (ohne Anspruch auf Vollständigkeit): AtmanAvr C (relativ günstig), KamAVR (kostenlos) VMLab (ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand der universellen und plattformunabhängigen Vorgehensweise mittels make und Makefiles näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind lediglich Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xff;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5a)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/;  // (5b)&lt;br /&gt;
   }                         // (5c)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (6)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In der mit (1) markierten Zeile, wird eine sogenannte Header-Datei eingebunden. In io.h sind die Registernamen definiert, die im späteren Verlauf genutzt werden.&lt;br /&gt;
&lt;br /&gt;
* Bei (2) beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion main.&lt;br /&gt;
&lt;br /&gt;
* Die Anschlüsse eines AVR (&amp;quot;Beinchen&amp;quot;) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Richtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B zu Ausgängen.&lt;br /&gt;
&lt;br /&gt;
* (4) stellt die Werte der Ausgänge ein. Die den ersten beiden  Bits des Ports zugeordnete Anschluss (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht-aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential).&lt;br /&gt;
&lt;br /&gt;
* (5) ist die so genannte Hauptschleife (main-loop). Dies ist eine Programmschleife, welche kontinuierlich wiederkehrende Befehle enthält. In diesem Beispiel ist sie leer. Der Controller durchläuft die Schleife immer wieder, ohne das etwas passiert (außer das Strom verbraucht wird). Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Wäre die Schleife nicht vorhanden, würde der Zustand des Controllers nach dem Programmende undefiniert.&lt;br /&gt;
&lt;br /&gt;
* (6) wäre das Programmende. Die Zeile ist nur aus Gründen der C- Kompatibilität enthalten: int main(void) besagt, dass die Funktion einen Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein auf den Controller lauffähiges Programm zu übersetzen,  wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen Makefile (ohne Endung) im selben Verzeichnis in dem auch die Datei main.c mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im folgenden Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\tmp\gcc_tut\quickstart&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei main.hex, in der der Code für den AVR enthalten ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.B. über In-System-Programming (ISP) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann (z.B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio), kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine LED leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode an einen dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Exkurs: Makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Plattformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.com) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das Makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den im Makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im Makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten Makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt [[AVR-GCC-Tutorial#EEPROM|Speicherzugriffe]]), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendeten Controllers gesetzt. Eine Liste der von avr-gcc und der avr-libc unterstützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien eintragen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
# nicht auskommentiert: EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Das erstellte Makefile und der Code müssen im gleichen Ordner sein, auch sollte der Dateiname nicht verändert werden. &lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem Makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschinencode erzeugt. Beim Update auf eine neue Compilerversion ist zu beachten, dass diese möglicherweise intern andere Optimierungsalgorithmen verwenden und sich dadurch die Größe des Machinencodes etwas ändert, ohne dass man im Quellcode etwas geändert hat.&lt;br /&gt;
&lt;br /&gt;
Als Orientierungswerte die Größe des Maschinencodes bei verschiedenen Optionen für einen nicht näher spezifizierten relativ kleinen Testcode bei Verwendung einer nicht näher spezifizierten Compilerversion. &lt;br /&gt;
&lt;br /&gt;
* -O0 : 12&#039;217 Byte&lt;br /&gt;
* -O1 : 9&#039;128 Byte&lt;br /&gt;
* -O2 : 1&#039;670 Byte&lt;br /&gt;
* -O3 : 3&#039;004 Byte&lt;br /&gt;
* -Os : 1&#039;695 Byte&lt;br /&gt;
&lt;br /&gt;
Im diesem Testfall führt die Option -O2 mit zum kompaktesten Code, dies  allerdings hier nur mit 25 Bytes &amp;quot;Vorsprung&amp;quot;. Es kann durchaus sein, dass nur wenige Programmerweiterungen dazu führen, dass Compilieren mit -Os wieder in kompakteren Code resultiert.&lt;br /&gt;
&lt;br /&gt;
siehe dazu auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/using_tools.html#gcc_optO avr-libc manual Abschnitt Using the gnu-tools/Compiler-Optionen]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_optflags avr-libc Manual FAQ Nr. 16] (Stand avr-libc Version 1.4.5)&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wird hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (großes S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Taktfrequenz ===&lt;br /&gt;
&lt;br /&gt;
Neuere Versionen der WinAVR/Mfile Vorlage für Makefiles beinhalten die Definition einer Variablen F_CPU (WinAVR 2/2005). Darin wird die Taktfrequenz des Controllers in Hertz eingetragen. Die Definition steht dann im gesamten Projekt ebenfalls unter der Bezeichnung F_CPU zur Verfügung (z.B. um daraus  UART-, SPI- oder ADC-Frequenzeinstellungen abzuleiten).&lt;br /&gt;
&lt;br /&gt;
Die Angabe hat rein &amp;quot;informativen&amp;quot; Charakter, die tatsächliche Taktrate wird über den externen Takt (z.B. Quarz) bzw. die Einstellung des internen R/C-Oszillators  bestimmt. Die Nutzung von F_CPU hat also nur Sinn, wenn die Angabe mit dem tatsächlichen Takt übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Innerhalb neuerer Versionen der avr-libc (ab Version 1.2) wird die Definition der Taktfrequenz (F_CPU) zur Berechnung der Wartefunktionen in delay.h genutzt. Diese funktionieren nur dann korrekt, wenn F_CPU mit der tatsächlichen Taktfrequenz übereinstimmt.&lt;br /&gt;
F_CPU muss dazu jedoch nicht unbedingt im makefile definiert werden. Es reicht aus, wird aber bei mehrfacher Anwendung unübersichtlich, vor &#039;&#039;#include &amp;lt;util/delay.h&amp;gt;&#039;&#039; (veraltet: &#039;&#039;#include &amp;lt;avr/delay.h&amp;gt;&#039;&#039;) ein &#039;&#039;#define F_CPU [hier Takt in Hz]UL&#039;&#039; einzufügen. Bei Nutzung von delay.h ist darauf zu achten, dass die Optimierung des Compilers nicht ausgeschaltet ist, sonst wird sehr viel Code erzeugt und die Wartezeit stimmt nicht mit der gewünschten Zeitspanne überein.&lt;br /&gt;
Vgl. dazu  den [http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html entsprechenden Abschnitt der Dokumentation].&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff: (sollte nur noch in Ausnahmefällen genutzt werden)&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
Die Verwendung von Makefiles bietet noch viele weiter Möglichkeiten, einige davon werden im Anhang [[AVR-GCC-Tutorial#Zus.C3.A4tzliche_Funktionen_im_Makefile|Zusätzliche Funktionen im Makfile]] erläutert.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige (Integer) Datentypen =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei stdint.h definiert. &lt;br /&gt;
Zur Nutzung der standardisierten Typen bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// ab avr-libc Version 1.2.0 möglich und empfohlen:&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// veraltet: #include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef signed char        int8_t;&lt;br /&gt;
typedef unsigned char      uint8_t;&lt;br /&gt;
&lt;br /&gt;
typedef short              int16_t;&lt;br /&gt;
typedef unsigned short     uint16_t;&lt;br /&gt;
&lt;br /&gt;
typedef long               int32_t;&lt;br /&gt;
typedef unsigned long      uint32_t;&lt;br /&gt;
&lt;br /&gt;
typedef long long          int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* int8_t steht für einen 8-Bit Integer mit einem Wertebereich -128 bis +127.&lt;br /&gt;
* uint8_t steht für einen 8-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 255&lt;br /&gt;
&lt;br /&gt;
* int16_t steht für einen 16-Bit Integer mit einem Wertebereich -32768 bis +32767.&lt;br /&gt;
* uint16_t steht für einen 16-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 65535.&lt;br /&gt;
&lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; werden vorzeichenbehaftete Zahlen abgespeichert. Typen mit vorgestelltem &#039;&#039;u&#039;&#039; dienen der Ablage von postiven Zahlen (inkl. 0).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/(Standard) Integer Types&lt;br /&gt;
&lt;br /&gt;
= Bitfelder =&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&lt;br /&gt;
&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in eine einzelne Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitfelder sparen Platz im RAM, zu Lasten von Platz im Flash, verschlechtern aber unter Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.&lt;br /&gt;
&lt;br /&gt;
Wenn man nur eine paar wenige Variablen vom Typ bool verwenden möchte, kann man&lt;br /&gt;
auch die Headerdatei &amp;lt;stdbool.h&amp;gt; einbinden und sich dann wie gewohnt einen Booltyp anlegen. Variablen dieses Typs brauchen dennoch 1 Byte Speicher, ermöglichen aber eine genaue Unterscheidung zwischen Zahlenvariable und boolscher Variable.&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;C-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum können für allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden&lt;br /&gt;
AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039;, wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output).  Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt.&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend&lt;br /&gt;
angepasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O-Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek (avr-libc) stellt auch Funktionen zur Abfrage eines einzelnen Bits eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist, wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind &#039;&#039;nicht erforderlich&#039;&#039;, man kann auch &amp;quot;einfache&amp;quot; C-Syntax verwenden, die universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) wenn das Bit gesetzt und 0 (&amp;quot;falsch&amp;quot;) wenn es nicht gesetzt ist. Die Negierung des Ausdrucks, also  &#039;&#039;&#039;!&#039;&#039;&#039;(Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)), entspricht &#039;&#039;bit_is_clear&#039;&#039; und gibt einen Wert &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) zurück, falls das Bit nicht gesetzt ist,&lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O-Register einfach wie eine Variable setzen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und outp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben von Bits ====&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausdruck:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit (für 1) eines Bytes hat die Bitnummer 0, das &amp;quot;höchstwertige&amp;quot; (für 128) die Nummer 7.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );    /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3);   /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die warten, bis ein bestimmter Zustand auf einem Bit erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend gewartet&amp;quot; wird. D.h., der Programmablauf bleibt an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den Watchdog ein, muss man darauf achten, dass dieser auch noch getriggert wird (zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Änderung: Habe PINx und PORTx vertauscht.PINx ist für Pull up und PORTx für high bzw low. Michael.L  - &lt;br /&gt;
&lt;br /&gt;
Was sollte diese Aenderung?? Den Rest des Abschnitts auch gelesen? Evtl. die hier nicht erlaeuterten Zusatzfunktion der neuen AVRs durcheinandergeworfen? Aenderung verworfen bis mehr Details.  m.thomas. &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
// Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
&lt;br /&gt;
DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
/* mehr Tipparbeit aber übersichtlicher: */&lt;br /&gt;
DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&lt;br /&gt;
/* Einige Compiler erlauben die Eingabe von Konstanten im &lt;br /&gt;
   Binärformat, z.B. gcc in WinAVR, NICHT aber aus&lt;br /&gt;
   GNU-gcc-Quellen selbst erstellte Compiler. Diese Schreib-&lt;br /&gt;
   weise also nicht nutzen, wenn Code mit anderen &lt;br /&gt;
   ausgetauscht werden soll. */&lt;br /&gt;
DDRB = 0b00011111;    /* direkte Zuweisung &lt;br /&gt;
                         - übersichtlicher &lt;br /&gt;
                         - nicht standardkonform &lt;br /&gt;
                         - selten portabel&lt;br /&gt;
                         - nur bei modifiziertem GCC */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
Weitere Beispiele:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
DDRB = 0xff; &lt;br /&gt;
// Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; DDB0 );&lt;br /&gt;
// Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( ( 1 &amp;lt;&amp;lt; DDB3 ) | ( 1&amp;lt;&amp;lt;DDB4) );&lt;br /&gt;
// Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB |= ( 1 &amp;lt;&amp;lt; DDB0) | ( 1 &amp;lt;&amp;lt; DDB3 );&lt;br /&gt;
// Alle Pins auf Eingang:&lt;br /&gt;
DDRB = 0x00;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&#039;&#039;&lt;br /&gt;
* &#039;&#039;zuerst die Bits im PORTx-Register setzen und&#039;&#039;&lt;br /&gt;
* &#039;&#039;anschließend die Datenrichtung auf Ausgang stellen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Daraus ergibt sich die Abfolge für einen Eingangspin:&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze PORTx: interner Pull-Up aktiv&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze DDRx: Ausgang &amp;quot;high&amp;quot;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bei der Reihenfolge erst DDRx und dann PORTx, kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;&#039;nicht&#039;&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, &#039;&#039;&#039;sondern&#039;&#039;&#039; Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.&amp;lt;/font&amp;gt; (Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände.)&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch.  Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVRs haben sogar an den meisten Pins softwaremäßig zuschaltbare interne Pull-Up Widerstände, welche wir natürlich auch verwenden können.&lt;br /&gt;
&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;DDC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== (Tasten-)Entprellung ====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim Schließen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&lt;br /&gt;
&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen als mehrfache Impulse gezählt wird. Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* bei alter avr-libc: #include &amp;lt;avr/delay.h&amp;gt; */      &lt;br /&gt;
&lt;br /&gt;
/* Einfache Funktion zum Entprellen eines Tasters */&lt;br /&gt;
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)&lt;br /&gt;
{&lt;br /&gt;
    if ( ! (*port &amp;amp; (1 &amp;lt;&amp;lt; pin)) )&lt;br /&gt;
    {&lt;br /&gt;
        /* Pin wurde auf Masse gezogen, 100ms warten   */&lt;br /&gt;
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz&lt;br /&gt;
        _delay_ms(50); &lt;br /&gt;
        if ( *port &amp;amp; (1 &amp;lt;&amp;lt; pin) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Anwender Zeit zum Loslassen des Tasters geben */&lt;br /&gt;
            _delay_ms(50);&lt;br /&gt;
            _delay_ms(50); &lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; PB0 );                 /* PIN PB0 auf Eingang (Taster)            */&lt;br /&gt;
    PORTB |= ( 1 &amp;lt;&amp;lt; PB0 );                 /* Pullup-Widerstand aktivieren            */&lt;br /&gt;
    ...&lt;br /&gt;
    if (debounce(&amp;amp;PINB, PB0))             /* Falls Taster an PIN PB0 gedrueckt..    */&lt;br /&gt;
        PORTD = PIND ^ ( 1 &amp;lt;&amp;lt; PD7 );  /* ..LED an Port PD7 an-&lt;br /&gt;
                                   bzw. ausschalten */&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesem Beispiel ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritische Anwendungen sollte man ein anderes Verfahren nutzen (z.B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).&lt;br /&gt;
&lt;br /&gt;
Zum Thema Entprellen siehe auch:&lt;br /&gt;
* Artikel [[Entprellung]]&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls ben&amp;amp;ouml;tigt, kann eine 16-Bit Variable auch recht einfach manuell in ihre zwei 8-Bit Bestandteile zerlegt werden. Folgendes Beispiel demonstriert dies anhand des pseudo- 16-Bit Registers UBRR.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
typedef union {&lt;br /&gt;
        uint16_t i16;&lt;br /&gt;
        struct {&lt;br /&gt;
                uint8_t i8l;&lt;br /&gt;
                uint8_t i8h;&lt;br /&gt;
        };&lt;br /&gt;
} convert16to8;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
convert16to8 baud;&lt;br /&gt;
baud.i16 = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
UBRRH = baud.i8h;&lt;br /&gt;
UBRRL = baud.i8l;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/*alternativ:*/&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint16_t wFoo16;&lt;br /&gt;
uint8_t bFooLow, bFooHigh;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
wFoo16   = 0xAA55;                 /* zu &amp;quot;zerlegende&amp;quot; 16Bit-Integer */&lt;br /&gt;
bFooHigh = (uint8_t)(wFoo16 &amp;gt;&amp;gt; 8); /* MS-Byte */&lt;br /&gt;
bFooLow  = (uint8_t)(wFoo16);      /* LS-Byte */&lt;br /&gt;
...&lt;br /&gt;
#define us0(Data) ((unsigned char *)(&amp;amp;Data))&lt;br /&gt;
#define us1(Data) ((unsigned char *)((&amp;amp;Data)+1))&lt;br /&gt;
bFooHigh = us1(wFoo16);&lt;br /&gt;
bFoolow  = us0(wFoo16);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen (z.B. ATmega8) teilen sich UBRRH und UCSRC die gleiche Memory-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL) welches Register tats&amp;amp;auml;chlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und &amp;amp;uuml;bergibt den Wert &#039;&#039;3&#039;&#039; und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und &amp;amp;uuml;bergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Bei einigen 16-bit Registern (insbesondere die der 16-bit Timer) erfolgen Schreibzugriffe über das sogenannte &#039;&#039;temporary Register&#039;&#039;. Die Reihenfolge der Zugriffe bestimmt, wann der Wert tatsächlich ins Register geschrieben wird. Typisch wird erst das High-Byte beschrieben (xxxH) und intern im temporary Register zwischengespeichert. Nachdem das Low-Byte (xxxL) geschrieben wurde, setzt der Controller mit diesem und dem im temporary Register zwischengespeicherten Wert für das High-Byte das 16-bit Register. Dabei ist zu beachten, dass intern nur ein temporary Register verfügbar ist, welches in Interruptroutinen mglw. mit einem anderen Wert überschrieben wird, wenn dort ebenfalls 16-bit Register beschrieben werden. avr-gcc/avr-libc berücksichtigen die korrekte Reihenfolge automatisch, wenn die Register mit ihrem &amp;quot;16-bit Label&amp;quot; (ohne H bzw. L) angesprochen werden, dabei ist der Schutz des temporary Registers vor Überschreiben durch Interruptroutinen dennoch zu beachten (im Zweifel beim Schreibzugriff die Interrupts kurzzeitig global deaktivieren).&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t key_pressed(const volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if ( last_state == ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) ) ) {&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    i = key_pressed( &amp;amp;PINB, PB1 );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Im Übrigen können mit volatile gekennzeichnete Variablen auch als const deklariert werden, um sicherzustellen, dass sie nur noch von der Hardware änderbar sind.&lt;br /&gt;
&lt;br /&gt;
= Der UART =&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Über den [[UART]] kann ein AVR leicht mit einer [[RS-232]]-Schnittstelle eines PC oder sonstiger Geräte mit &amp;quot;serieller Schnittstelle&amp;quot; verbunden werden. &lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungen des UART:&lt;br /&gt;
&lt;br /&gt;
* Debug-Schnittstelle: z.B. zur Anzeige von Zwischenergebnissen (&amp;quot;printf-debugging&amp;quot; - hier besser &amp;quot;UART-debugging&amp;quot;) auf einem PC. Auf dem Rechner reicht dazu ein [[RS-232#Terminalprogramme|Terminalprogramm]] (MS-Windows: Hyperterm oder besser Bray-Terminal, [http://www.mikrocontroller.net/forum/read-8-155472.html#new HTerm]; Unix/Linux z.B. minicom). Ein direkter Anschluss ist aufgrund unterschiedlicher Pegel nicht möglich, jedoch sind entsprechende Schnittstellen-ICs wie z.B. ein MAX232 günstig und leicht zu integrieren. Rechner ohne serielle Schnittstelle können über fertige USB-seriell-Adapter angeschlossen werden. &lt;br /&gt;
* &amp;quot;Mensch-Maschine Schnittstelle&amp;quot;: z.B. Konfiguration und Statusabfrage über eine &amp;quot;Kommandozeile&amp;quot; oder Menüs (siehe z.B. Forumsbeitrag [http://www.mikrocontroller.net/topic/52985 Auswertung RS232-Befehle]) &lt;br /&gt;
* Übertragen von gespeicherten Werten: z.B. bei einem Datenlogger&lt;br /&gt;
* Anschluss von Geräten mit serieller Schnittstelle (z.B. (Funk-)Modems, Mobiltelefone, Drucker, Sensoren, &amp;quot;intelligente&amp;quot; LC-Displays, GPS-Empfänger). &lt;br /&gt;
* &amp;quot;Feldbusse&amp;quot; auf RS485/RS422-Basis mittels entsprechenden Bustreiberbausteinen (z.B. MAX485)&lt;br /&gt;
* DMX, Midi etc.&lt;br /&gt;
* LIN-Bus (&#039;&#039;&#039;L&#039;&#039;&#039;ocal &#039;&#039;&#039;I&#039;&#039;&#039;nterconnect &#039;&#039;&#039;N&#039;&#039;&#039;etwork): Preiswerte Sensoren/Aktoren in der Automobiltechnik und darüber hinaus&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben ein bis zwei vollduplexfähigen UART (&#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) schon eingebaut (&amp;quot;Hardware-UART&amp;quot;). &lt;br /&gt;
Übrigens: Vollduplex heißt nichts anderes, als dass der Baustein gleichzeitig senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega, ATtiny) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterscheidet sich vom UART hauptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurationsregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit zeigt an, ob der Sendepuffer bereit ist, um ein zu sendendes Zeichen aufzunehmen. Das Bit wird vom AVR gesetzt (1), wenn der Sendepuffer leer ist. Es wird gelöscht (0), wenn ein Zeichen im Sendedatenregister vorhanden ist und noch nicht in das Sendeschieberegister übernommen wurde. Atmel empfiehlt aus Kompatibilitätsgründen mit kommenden µC, UDRE auf 0 zu setzen, wenn das UCSRA Register beschrieben wird.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
\mathrm{UBRR} = \frac{\mathrm{Taktfrequenz}}{\mathrm{Baudrate} \cdot 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS-232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. &lt;br /&gt;
&amp;lt;!-- &amp;quot;Hackerloesung&amp;quot; auskommentiert - nicht so gut in einem &amp;quot;Einsteiger-Tutorial&amp;quot; - mthomas&lt;br /&gt;
Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfiehlt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== UART initialisieren ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben. Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach gewünschter Funktionsweise die benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Da wir vorerst nur senden möchten und noch keine Interrupts auswerten wollen, gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs mit USART haben mehrere Konfigurationsregister und erfordern eine etwas andere Konfiguration. Für einen ATmega16 z.B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                 // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);     // Asynchron 8N1&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist noch das Baudratenregister &#039;&#039;&#039;UBRR&#039;&#039;&#039; einzustellen. Bei neueren AVRs besteht es aus zwei Registern &#039;&#039;&#039;UBRRL&#039;&#039;&#039; und &#039;&#039;&#039;UBRRH&#039;&#039;&#039;. Der Wert dafür ergibt sich aus der angegebenen Formel durch Einsetzen der Taktfrequenz und der gewünschten Übertragungsrate. Das Berechnen der Formel wird dem [[C-Präprozessor|Präprozessor]] überlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann&lt;br /&gt;
   F_CPU im Makefile definiert werden, eine nochmalige Definition&lt;br /&gt;
   hier wuerde zu einer Compilerwarnung fuehren. Daher &amp;quot;Schutz&amp;quot; durch&lt;br /&gt;
   #ifndef/#endif &lt;br /&gt;
&lt;br /&gt;
   Dieser &amp;quot;Schutz&amp;quot; kann zu Debugsessions führen, wenn AVRStudio &lt;br /&gt;
   verwendet wird und dort eine andere, nicht zur Hardware passende &lt;br /&gt;
   Taktrate eingestellt ist: Dann wird die folgende Definition &lt;br /&gt;
   nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) &lt;br /&gt;
   von AVRStudio - daher Ausgabe einer Warnung falls F_CPU&lt;br /&gt;
   noch nicht definiert: */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000&amp;quot;&lt;br /&gt;
#define F_CPU 4000000UL    // Systemtakt in Hz &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600UL          // Baudrate&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) // Fehler in Promille, 1000 = kein Fehler.&lt;br /&gt;
&lt;br /&gt;
#if ((BAUD_ERROR&amp;lt;990) || (BAUD_ERROR&amp;gt;1010))&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;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    UBRR = UBRR_VAL;&lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&amp;lt;!-- mthomas: warum nicht UL?, wird von AVRStudio auch mit UL übergeben --&amp;gt;&lt;br /&gt;
Wieder für den Mega16 mit zwei Registern für die Baudrateneinstellung eine etwas andere Programmierung. Wichtig ist, dass UBRRH &#039;&#039;&#039;vor&#039;&#039;&#039; UBRRL geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmega16 */&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einige AVR (z.B. ATmega169, ATmega48/88/168, AT90CAN jedoch nicht für z.B. ATmega16/32, ATmega128, ATtiny2313) wird durch die Registerdefinitionen der avr-libc (io*.h) auch für Controller mit zwei UBRR-Registern (UBRRL/UBRRH) ein UBRR bzw. UBRR0 als &amp;quot;16-bit-Register&amp;quot; definiert und man kann auch Werte direkt per UBRR = UBRR_VAL zuweisen. Intern werden dann zwei Zuweisungen für UBRRH und UBRRL generiert. Dies ist nicht bei allen Controllern möglich, da die beiden Register nicht bei allen aufeinanderfolgende Addressen aufweisen. Die getrennte Zuweisung an UBRRH und UBRRL wie im Beispiel gezeigt ist universeller und portabler und daher vorzuziehen.&lt;br /&gt;
&lt;br /&gt;
Die Makros sind sehr praktisch, da sie sowohl automatisch den Wert für UBRR als auch den Fehler in der generierten Baudrate berechnen und im Falle einer Überschreitung (+/-1%) einen Fehler und somit Abbruch im Compilerablauf generieren. Damit können viele Probleme mit &amp;quot;UART sendet komische Zeichen&amp;quot; vermieden werden. Ausserdem kann man mühelos die Einstellung an eine neue Taktfrequenz bzw. Baudrate anpassen, ohne selber rechnen oder in Tabellen nachschlagen zu müssen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. Vorher ist zu prüfen, ob das UART-Modul bereit ist, das zu sendende Zeichen entgegenzunehmen. Die Bezeichnungen des/der Statusregisters mit dem Bit UDRE ist abhängig vom Controllertypen (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    // bei AVR mit einem UART (&amp;quot;classic AVR&amp;quot; z.B. AT90S8515)&lt;br /&gt;
    while (!(USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                  /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&lt;br /&gt;
    /** ODER **/&lt;br /&gt;
&lt;br /&gt;
    // bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                    /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe &amp;quot;String senden&amp;quot; wird durch zwei Funktionen abgearbeitet. Die universelle/controllerunabhängige Funktion uart_puts übergibt jeweils ein Zeichen der Zeichenkette an eine Funktion uart_putc, die abhängig von der vorhandenen Hardware implementiert werden muss. In der Funktion zum Senden eines Zeichens ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// putc fuer AVR mit einem UART (z.B. AT90S8515)&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while(!(USR &amp;amp; (1 &amp;lt;&amp;lt; UDRE)))  /* warte, bis UDR bereit */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = c;                     /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** ODER **/&lt;br /&gt;
&lt;br /&gt;
// bei neueren AVRs andere Bezeichnung fuer die Statusregister, hier ATmega16:&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich */&lt;br /&gt;
    {&lt;br /&gt;
    }                             &lt;br /&gt;
&lt;br /&gt;
    UDR = c;                      /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* puts ist unabhaengig vom Controllertyp */&lt;br /&gt;
void uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != &#039;\0&#039; also ungleich dem &amp;quot;String-Endezeichen&amp;quot; */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die in uart_putc verwendeten Schleifen, in denen gewartet wird bis die UART-Hardware zum senden bereit ist, sind insofern etwas kritisch, da während des Sendens eines Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. empfangenen Zeichen/Bytes zwischengespeichert und in Interruptroutinen an die U(S)ART-Hardware weitergegeben bzw. von ihr ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&lt;br /&gt;
&lt;br /&gt;
=== Schreiben von Variableninhalten ===&lt;br /&gt;
&lt;br /&gt;
Sollen Inhalte von Variablen (Ganzzahlen, Fließkomma) in &amp;quot;menschenlesbarer&amp;quot; Form gesendet werden, ist vor dem Transfer eine Umwandlung in Zeichen (&amp;quot;ASCII&amp;quot;) erforderlich. Bei nur einer Ziffer ist diese Umwandlung relativ einfach: man addiert den ASCII-Wert von Null zur Ziffer und kann diesen Wert direkt senden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_putc (s.o.)&lt;br /&gt;
&lt;br /&gt;
// Ausgabe von 0123456789&lt;br /&gt;
char c;&lt;br /&gt;
&lt;br /&gt;
for (uint8_t i=0; i&amp;lt;=9; ++i) {&lt;br /&gt;
    c = i + &#039;0&#039;;&lt;br /&gt;
    uart_putc( c );&lt;br /&gt;
    // verkuerzt: uart_putc( i + &#039;0&#039; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll mehr als eine Ziffer ausgegeben werden, bedient man sich zweckmäßigerweise vorhandener Funktionen zur Umwandlung von Zahlen in Zeichenketten/Strings. Die Funktion der avr-libc zur Umwandlung von vorzeichenbehafteten 16bit-Ganzzahlen (int16_t) in Zeichenketten heißt &#039;&#039;itoa&#039;&#039; (Integer to ASCII). Man muss der Funktion einen Speicherbereich zur Verarbeitung (buffer) mit Platz für alle Ziffern, das String-Endezeichen (&#039;\0&#039;) und evtl. das Vorzeichen bereitstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   char s[7];&lt;br /&gt;
   int16_t i = -12345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   itoa( i, s, 10 ); // 10 fuer radix -&amp;gt; Dezimalsystem&lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // da itoa einen Zeiger auf den Beginn von s zurueckgibt verkuerzt auch:&lt;br /&gt;
   uart_puts( itoa( i, s, 10 ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für vorzeichenlose 16bit-Ganzzahlen (uint16_t) exisitert &#039;&#039;utoa&#039;&#039;. Die Funktionen für 32bit-Ganzzahlen (int32_t und uint32_t) heißen &#039;&#039;ltoa&#039;&#039; bzw. &#039;&#039;ultoa&#039;&#039;. Da 32bit-Ganzzahlen mehr Stellen aufweisen können, ist ein entsprechend größerer Pufferspeicher vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Auch Fließkommazahlen (float/double) können mit breits vorhandenen Funktionen in Zeichenfolgen umgewandelt werden, dazu existieren die Funktionen &#039;&#039;dtostre&#039;&#039; und &#039;&#039;dtostrf&#039;&#039;. dtostre nutzt Exponentialschreibweise (&amp;quot;engineering&amp;quot;-Format). (Hinweis: z.Zt. existiert im avr-gcc kein &amp;quot;echtes&amp;quot; double, intern wird immer mit &amp;quot;einfacher Genauigkeit&amp;quot;, entsprechend float, gerechnet.) &lt;br /&gt;
&lt;br /&gt;
dtostrf und dtostre benötigen die libm.a der avr-libc. Bei Nutzung von Makefiles ist der Parameter -lm in in LDFLAGS anzugeben (Standard in den WinAVR/mfile-Makefilevorlagen). Nutzt man AVRStudio als IDE für den GNU-Compiler (gcc-Plugin) ist die libm.a unter Libaries auszuwählen: Project -&amp;gt; Configurations Options -&amp;gt; Libaries -&amp;gt; libm.a mit dem Pfeil nach rechts einbinden. Siehe auch die [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|FAQ]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
/* lt. avr-libc Dokumentation:&lt;br /&gt;
char* dtostrf(&lt;br /&gt;
  double __val,&lt;br /&gt;
  char   __width,&lt;br /&gt;
  char   __prec,&lt;br /&gt;
  char * __s&lt;br /&gt;
)  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   // Pufferspeicher ausreichend groß&lt;br /&gt;
   // evtl. Vorzeichen + width + Endezeichen:&lt;br /&gt;
   char s[8]; &lt;br /&gt;
   float f = -12.345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   dtostrf( f, 6, 3, s ); &lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // verkürzt: uart_puts( dtostrf( f, 7, 3, s ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfangen ==&lt;br /&gt;
=== einzelne Zeichen empfangen ===&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von Zeichen muss der Empfangsteil des UART bei der Initialisierung aktiviert werden, indem das RXEN-Bit im jeweiligen Konfigurationsregister (UCSRB bzw UCSR0B/UCSR1B) gesetzt wird. Im einfachsten Fall wird solange gewartet, bis ein Zeichen empfangen wurde, dieses steht dann im UART-Datenregister (UDR bzw. UDR0 und UDR1 bei AVRs mit 2 UARTS) zur Verfügung (sogen. &amp;quot;Polling-Betrieb&amp;quot;). Ein Beispiel für den ATmega16:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Zusaetzlich zur Baudrateneinstellung und der weiteren Initialisierung: */&lt;br /&gt;
void Usart_EnableRX()&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= ( 1 &amp;lt;&amp;lt; RXEN );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion blockiert den Programmablauf. Alternativ kann das RXC-Bit in einer Programmschleife abgefragt werden und dann nur bei gesetztem RXC-Bit UDR ausgelesen werden. Eleganter und in den meisten Anwendungsfällen &amp;quot;stabiler&amp;quot; ist die Vorgehensweise, die empfangenen Zeichen in einer Interrupt-Routine einzulesen und zur späteren Verarbeitung in einem Eingangsbuffer (FIFO-Buffer) zwischenzuspeichern. Dazu existieren fertige und gut getestete [[Libraries|Bibliotheken]] &amp;lt;!-- &amp;quot;echte Libraries&amp;quot; (.a) wie im Verweis beschrieben sind hier eigentlich nicht gemeint, verwirrt hier etwas, da AVR-&amp;quot;Libraries&amp;quot; meist per #defines anpassbare Source-Codes sind, vielleicht so: --&amp;gt; und Quellcodekomponenten (z.B. UART-Library von P. Fleury, procyon-avrlib und einige in der &amp;quot;Academy&amp;quot; von avrfreaks.net).&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html Dokumenation der avr-libc/stdlib.h]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_printf Die Nutzung von printf]&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&amp;lt;!-- nimmermehr: * siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (printf etc.) im [[AVR-Tutorial:_UART]]. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: 9bit&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Zeichenketten (Strings) ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Zeichenketten, muß man sich zunächst darüber im klaren sein, daß es ein Kriterium geben muß, an dem der µC erkennen kann, wann ein String zu Ende ist. Sehr oft wird dazu das Zeichen &#039;Return&#039; benutzt, um das Ende eines Strings zu markieren. Dies ist vom Benutzer einfach eingebbar und er ist auch daran gewöhnt, daß er eine Eingabezeile mit einem Druck auf die Return Taste abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Prinzipiell gibt es jedoch keine Einschränkung bezüglich dieses speziellen Zeichens. Es muß nur sichergestellt werden, daß dieses spezielle &#039;Ende eines Strings&#039; - Zeichen nicht mit einem im Text vorkommenden Zeichen verwechselt werden kann. Wenn also im zu übertragenden Text beispielsweise kein &#039;;&#039; vorkommt, dann spricht nichts dagegen, einen String mit einem &#039;;&#039; abschließen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird die durchaus übliche Annahme getroffen, daß eine Stringübertragung identisch ist mit der Übertragung einer Textzeile und daher mit einem Return (&#039;\n&#039;) abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Das Problem der Übertragung eines Strings reduziert sich damit auf die Aufgabenstellung: Empfange und Sammle Zeichen in einem char Array, bis entweder das Array voll ist oder das &#039;String Ende Zeichen&#039; empfangen wurde. Danach wird der empfangene Text noch mit einem &#039;\0&#039; Zeichen abgeschlossen um einen Standard C-String daraus zu machen, mit dem dann weiter gearbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_gets( char* Buffer, uint8_t MaxLen )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t NextChar;&lt;br /&gt;
  uint8_t StringLen = 0;&lt;br /&gt;
&lt;br /&gt;
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen&lt;br /&gt;
&lt;br /&gt;
                                  // Sammle solange Zeichen, bis:&lt;br /&gt;
                                  // * entweder das String Ende Zeichen kam&lt;br /&gt;
                                  // * oder das aufnehmende Array voll ist&lt;br /&gt;
  while( NextChar != &#039;\n&#039; &amp;amp;&amp;amp; StringLen &amp;lt; MaxLen - 1 ) {&lt;br /&gt;
    *Buffer++ = NextChar;&lt;br /&gt;
    StringLen++;&lt;br /&gt;
    NextChar = uart_getc();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
                                  // Noch ein &#039;\0&#039; anhängen um einen Standard&lt;br /&gt;
                                  // C-String daraus zu machen&lt;br /&gt;
  *Buffer = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf ist darauf zu achten, dass das empfangende Array auch mit einer&lt;br /&gt;
vernünftigen Größe definiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
  char Line[40];      // String mit maximal 39 zeichen&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) );&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Interruptbetrieb==&lt;br /&gt;
&lt;br /&gt;
Beim ATMEGA8 ist es möglich, dass RXCIE Bit im Register UCSRB zu setzen.&lt;br /&gt;
Dieser Interrupt wird immer ausglöst, wenn Daten erfolgreich empfangen wurden.&lt;br /&gt;
Zusätzlich braucht man die Routine:&lt;br /&gt;
&lt;br /&gt;
ISR (USART_RXC_vect) {&lt;br /&gt;
   //irgendein Code&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
natürlich muss &amp;quot;Global Interrupt Enable&amp;quot; Aktiviert sein.&lt;br /&gt;
!! Nur getestet beim ATMEGA8 !!&lt;br /&gt;
&lt;br /&gt;
[BAUSTELLE! Aus Lerngründen eventuell als eigenen UART-Interrupt-Block hinter den grundlegenden Interrupt-Teil im Tutorial und hier eine kurze Einführung und einen Verweis darauf anbieten.]&lt;br /&gt;
&lt;br /&gt;
* Unterschied Polling-Betrieb (bisher, oben) und Interrupt-Betrieb&lt;br /&gt;
* Empfangen (Receive)&lt;br /&gt;
** Verändertes UART-Init, ISR (RXC), ggf. Fallstricke ([http://www.mikrocontroller.net/topic/84256#707214 UDR in der ISR lesen!]), Philosophie einer ISR (kurz und schmerzlos), Datenaustausch ISR zu Restprogramm (volatile)&lt;br /&gt;
** Einfachstbeispiel ([http://www.mikrocontroller.net/topic/84228#707052 Echo] (noch buggy beim Datenzugriff, siehe Lit. 2+3!)), ggf. LED zur ISR-Empfangsanzeige oder Overflow-Anzeige&lt;br /&gt;
* FIFO-Puffer, Ringpuffer&lt;br /&gt;
* Senden (Transmit)&lt;br /&gt;
** Variante &amp;quot;UART Data Register Empty&amp;quot; (UDRE) &lt;br /&gt;
** Variante &amp;quot;UART Transmit Complete&amp;quot; (TXC) &lt;br /&gt;
* Fertige UART-Bibliotheken (-&amp;gt;Fleury, -&amp;gt;Procyon)&lt;br /&gt;
* Literatur: &lt;br /&gt;
** 1/ [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=48188 avrfreaks.net Tutorial] inkl. Diskussion (engl.)&lt;br /&gt;
** 2/ avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_16bitio Why do some 16-bit timer registers sometimes get trashed?]&lt;br /&gt;
** 3/ [[Interrupt]] und atomarer Datenzugriff&lt;br /&gt;
&lt;br /&gt;
==Software-UART==&lt;br /&gt;
&lt;br /&gt;
Falls die Zahl der vorhandenen Hardware-UARTs nicht ausreicht, können weitere Schnittstellen über sogennante Software-UARTs ergänzt werden. Es gibt dazu (mindestens) zwei Ansätze: &lt;br /&gt;
* Der bei AVRs üblichste Ansatz basiert auf dem Prinzip, dass ein externer Interrupt-Pin für den Empfang (&amp;quot;RX&amp;quot;) genutzt wird. Das Startbit löst den Interrupt aus, in der Interrupt-Routine (ISR) wird der externe Interrupt deaktiviert und ein Timer aktiviert. In der Interrupt-Routine des Timers wird der Zustand des Empfangs-Pins entsprechend der Baudrate abgetastet. Nach Empfang des Stop-Bits wird der externe Interrupt wieder aktiviert. Senden kann über einen beliebigen Pin (&amp;quot;TX&amp;quot;) erfolgen, der entsprechend der Baudrate und dem zu sendenden Zeichen auf 0 oder 1 gesetzt wird. Die Implementierung ist nicht ganz einfach, es existieren dazu aber fertige Bibliotheken (z.B. bei [http://www.avrfreaks.net/ avrfreaks] oder in der [http://hubbard.engr.scu.edu/embedded/avr/avrlib/ Procyon avrlib]).&lt;br /&gt;
* Ein weiterer Ansatz erfordert keinen Pin mit &amp;quot;Interrupt-Funktion&amp;quot; aber benötigt mehr Rechenzeit. Jeder Input-Pin kann als Empfangspin (RX) dienen. Über einen Timer wird der Zustand des RX-Pins mit einem vielfachen der Baudrate abgetastet (dreifach scheint üblich) und High- bzw. Lowbits anhand einer Mindestanzahl identifiziert. (Beispiel: &amp;quot;Generic Software Uart&amp;quot; Application-Note von IAR)&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (z.B. ATtiny26 oder ATmega48,88,168,169) verfügen über ein Universal Serial Interface (USI), das teilweise UART-Funktion übernehmen kann. Atmel stellt eine Application-Note bereit, in der die Nutzung des USI als UART erläutert wird (im Prinzip &amp;quot;Hardware-unterstützter Software-UART&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==Handshaking==&lt;br /&gt;
Wenn der Sender ständig sendet, wird irgendwann der Fall eintreten, daß der Empfänger nicht bereit ist, neue Zeichen zu empfangen. In diesem Fall muß durch ein &#039;&#039;&#039;Handshake-Verfahren&#039;&#039;&#039; die Situation bereinigt werden. Handshake bedeutet nichts anderes, als daß der Empfänger dem Sender mitteilt, daß er zur Zeit keine Daten annehmen kann und der Sender die Übertragung der nächsten Zeichen solange einstellen soll, bis der Empfänger signalisiert, daß er wieder Zeichen aufnehmen kann.&lt;br /&gt;
===Hardwarehandshake (RTS/CTS)===&lt;br /&gt;
Beim Hardwarehandshake werden zusätzlich zu den beiden Daten-Übertragungsleitungen noch 2 weitere Leitungen benötigt: &#039;&#039;&#039;RTS&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;equest &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end) und &#039;&#039;&#039;CTS&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end). Jeder der beiden Kommunikationspartner ist verpflichtet, bevor ein Zeichen gesendet wird, den Zustand der &#039;&#039;&#039;RTS&#039;&#039;&#039; Leitung zu überprüfen. Nur wenn die Gegenstelle darauf Empfangsbereitschaft signalisiert, darf das Zeichen gesendet werden. Um der Gegenstelle zu signalisieren, daß sie zur Zeit keine Zeichen schicken soll, wird die Leitung &#039;&#039;&#039;CTS&#039;&#039;&#039; benutzt.&lt;br /&gt;
&lt;br /&gt;
===Softwarehandshake (XON/XOFF)===&lt;br /&gt;
Beim Softwarehandshake sind keine speziellen Leitungen notwendig. Statt dessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, daß Senden einzustellen bzw. wieder aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;XOFF&#039;&#039;&#039; Aufforderung das Senden einzustellen&lt;br /&gt;
* &#039;&#039;&#039;XON&#039;&#039;&#039;  Gegenstelle darf wieder senden&lt;br /&gt;
&lt;br /&gt;
Nachteilig bei einem Softwarehandshake ist es, daß dadurch keine direkte binäre Datenübertragung mehr möglich ist. Von den möglichen 256 Bytewerten werden ja 2 (nämlich &#039;&#039;&#039;XON&#039;&#039;&#039; und &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus.&lt;br /&gt;
&lt;br /&gt;
==Fehlersuche==&lt;br /&gt;
Erstaunlich oft wird im Forum der Hilferuf laut: &amp;quot;Meine UART funktioniert nicht, was mache ich falsch&amp;quot;. In der überwiegenden Mehrzahl der Fälle stellt sich dann heraus, daß es sich um ein Hardwareproblem handelt, wobei da wiederrum der Löwenanteil auf das Konto einer nicht korrekt eingestellten Taktrate geht: Der µC benutzt nicht einen angeschlossenen Quarz, so wie er auch im Programm eingetragen ist, sondern läuft immer noch mit dem internen RC-Takt. Daraus resultiert aber auch, daß der Baudraten Konfigurationswert falsch berechnet wird.&lt;br /&gt;
&lt;br /&gt;
Eine Checkliste zum Aufspüren solcher Fehler findet sich [[AVR_Checkliste#UART.2FUSART|hier]].&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Analoge Eingangswerte werden in der Regel über den AVR Analog-Digital-Converter (AD-Wandler, ADC) eingelesen, der in vielen Typen verfügbar ist (typisch 10bit Auflösung). Durch diesen werden analogen Signale (Spannungen) in digitale Zahlenwerte gewandelt. Bei AVRs, die über keinen internen AD-Wandler verfügen (z.B. ATmega162, ATtiny2313), kann durch externe Beschaltung (R/C-Netzwerk und &amp;quot;Zeitmessung&amp;quot;) die Funktion des AD-Wandlers &amp;quot;emuliert&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
Es existieren keine AVRs mit eingebautem Digital-Analog-Konverter (DAC). Diese Funktion muss durch externe Komponenten nachgebildet werden (z.B. PWM und &amp;quot;Glättung&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Unabhängig davon besteht natürlich immer die Möglichkeit, spezielle Bausteine zur Analog-Digital- bzw. Digital-Analog-Wandlung zu nutzen und diese über eine digitale Schnittstelle (z.b. SPI oder I2C) mit einem AVR anzusteuern.&lt;br /&gt;
&lt;br /&gt;
== AC (Analog Comparator) ==&lt;br /&gt;
&lt;br /&gt;
Der Comparator vergleicht 2 Spannungen an den Pins AIN0 und AIN1 und gibt einen Status aus welche der beiden Spannungen größer ist. AIN0 Dient dabei als Referenzspannung (Sollwert) und AIN1 als Vergleichsspannung (Istwert). Als Referenzspannung kann auch alternativ eine interne Referenzspannung ausgewählt werden.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Liegt die Vergleichsspannung (IST) unter der der Referenzspannung (SOLL) gibt der Comperator eine logische 0 aus. ISt die Vergleichsspannung hingegen größer als die Referenzspannung wird eine logische 1 ausgegeben.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Der Comperator arbeitet völlig autark bzw. parallel zum Prozessor. Für mobile Anwendungen empfiehlt es sich ihn abzuschalten sofern er nicht benötigt wird, da er ansonsten Strom benötigt. Der Comparator kann Interruptgesteuert abgefragt werden oder im Pollingbetrieb.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Steuer- bzw. Statusregister ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ACSR (0x28) - Analog Comparator Status Register&amp;lt;BR&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACD&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;AINBG&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACO&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACI&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | n/a&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 7 ACD - Analog Comperator Disable: 0 = Comparator ein, 1 = Comparator aus. Wird dieses Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 ACIE ggf. abgeschaltet werden.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 6 AINBG - Analog Comparator Bandgap Select: Ermöglicht das umschalten zwischen interner und externer Referenzspannung. 1 = interne (~1,3 Volt), 0 = externe Referenzspannung (an Pin AIN0)&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 5 ACO - Analog Coparator Output: Hier wird das Ergebnis des Vergleichs angezeigt. Es liegt typischerweise nach 1-2 Taktzyklen vor. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ergebnis: &amp;lt;BR&amp;gt;IST &amp;lt; SOLL --&amp;gt; 0&amp;lt;BR&amp;gt;&lt;br /&gt;
IST &amp;gt; SOLL --&amp;gt; 1&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4 ACI - Analog Comparator Interrupt Flag: Dieses Bit wird von der Hardware gesetzt wenn ein Interruptereignis das in Bit 0 und 1 definiert ist eintritt. Dieses Bit löst noch keinen Interrupt aus! Die Interruptroutine wird nur dann ausgeführt wenn das Bit 3 ACIE gesetzt ist und global Interrupts erlaubt sind (I-Bit in SREG=1). Das Bit 4 ACI wird wieder gelöscht wenn die Interruptroutine ausgeführt wurde oder wenn manuell das Bit auf 1 gesetzt wird. Das Bit kann für Abfragen genutzt werden, steuert oder konfuguriert aber nicht den Comparator.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 3 ACIE - Analog Comparator Interrupt Enable: Ist das Bit auf 1 gesetzt wird immer ein Interrupt ausgelöst wenn das Ereignis das in Bit 1 und 0 definiert ist eintritt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 2 ACIC - Analog Comparator Input Capture Enable: Wird das Bit gesetzt wird der Comparatorausgang intern mit dem Counter 1 verbunden. Es könnten damit z.b. die Anzahl der Vergleiche im Counter1 gezählt werden. Um den Comparator an den Timer1 Input Capture Interrupt zu verbinden muß im Timerregister das TICIE1 Bit auf 1 gesetzt werden. Der Trigger wird immer dann ausgelöst wenn das in Bit 1 und 0 definierte Ereignis eintritt.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 1,0 ACIS1,ACIS0 - Analog Comparator Interrupt select: Hier wird definiert welche Ereignisse einen Interrupt auslösen sollen:&amp;lt;BR&amp;gt;&lt;br /&gt;
00 = Interrupt auslösen bei jedem Flankenwechsel&amp;lt;BR&amp;gt;&lt;br /&gt;
10 = Interrupt auslösen bei fallender Flanke&amp;lt;BR&amp;gt;&lt;br /&gt;
11 = Interrupt auslösen bei steigender Flanke&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Werden diese Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 gelöscht werden.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der Analog-Digital-Konverter (ADC) wandelt analoge Signale in digitale Werte um, welche vom Controller interpretiert werden können. Einige AVR-Typen haben bereits einen mehrkanaligen Analog-Digital-Konverter eingebaut. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst werden kann, wird durch die Auflösung des ADC in Anzahl Bits angegeben, man hört bzw. liest jeweils von 8-Bit-ADC oder 10-Bit-ADC oder noch höher. ADCs die in AVRs enthalten sind haben zur Zeit eine maximale Auflösung von 10-Bit.&lt;br /&gt;
&lt;br /&gt;
Ein ADC mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des Analog-Digital-Konverters ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Der interne ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem Analog-Digital-Wandler (ADC) zurückgreifen, die über mehrere Kanäle verfügen. Kanäle heißt in diesem Zusammenhang, dass zwar bis zu zehn analoge Eingänge am AVR verfügbar sind, aber nur ein &amp;quot;echter&amp;quot; Analog-Digital-Wandler zur Verfügung steht, vor der eigentlichen Messung ist also einzustellen, welcher Kanal (&amp;quot;Pin&amp;quot;) mit dem Wandler verbunden und gemessen wird.&lt;br /&gt;
&lt;br /&gt;
Die Umwandlung innerhalb des AVR basiert auf der schrittweisen Näherung. Beim AVR müssen die Pins &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AVCC&#039;&#039;&#039; beschaltet werden. Für genaue Messungen sollte AVCC über ein L-C Netzwerk mit VCC verbunden werden, um Spannungsspitzen und -einbrüche vom Analog-Digital-Wandler fernzuhalten. Im Datenblatt findet sich dazu eine Schaltung, die 10uH und 100nF vorsieht.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Analog-Digital-Wandlung wird auf eine Referenzspannung bezogen. Aktuelle AVRs bieten drei Möglichkeiten zur Wahl dieser Spannung:&lt;br /&gt;
&lt;br /&gt;
* Eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; am Anschlusspin &#039;&#039;&#039;AREF&#039;&#039;&#039;. Die minimale (externe) Referenzspannung darf jedoch nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. &lt;br /&gt;
&lt;br /&gt;
* Verfügt der AVR über eine interne Referenzspannung, kann diese genutzt werden. Alle aktuellen AVRs mit internem AD-Wandler sollten damit ausgestattet sein (vgl. Datenblatt: 2,56V oder 1,1V je nach Typ). Das Datenblatt gibt auch über die Genauigkeit dieser Spannung Auskunft.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;interne&#039;&#039;&#039; Referenzspannung wird auf &#039;&#039;&#039;Vcc&#039;&#039;&#039; bezogen, eine externe Referenzspannung auf &#039;&#039;&#039;GND (Masse)&#039;&#039;&#039;. Davon unabhängig werden digitalisierte Spannungen &#039;&#039;&#039;immer&#039;&#039;&#039; auf GND bezogen. Beim ATMEGA8 z.B. ist der Pin AREF über 32kOhm mit GND verbunden, d.h. man muss diese doch extrem niedrige Eingangsimpedanz mit in die Berechnung für einen Spannungsteiler einbeziehen, bzw. kann diesen Widerstand als R2 gleich mit benutzen. Formel für Spannungsteiler: Udiv = U / ((R1 + R2) / R2)&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung von Vcc oder der internen Referenz wird empfohlen, einen Kondensator zwischen dem AREF-Pin und GND anzuordnen. Die Festlegung, welche Spannungsreferenz genutzt wird, erfolgt z.B. beim ATmega16 mit den Bits REFS1/REFS0 im ADMUX-Register. Die zu messende Spannung muss im Bereich zwischen &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AREF&#039;&#039;&#039; (egal ob intern oder extern) liegen. &lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; kann in zwei verschiedenen Betriebsarten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
; Einfache Wandlung (Single Conversion) : In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestoßen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
; Frei laufend (Free Running) : In dieser Betriebsart erfasst der Wandler permanent die anliegende Spannung und schreibt diese in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Die Register des ADC ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; verfügt über eigene Register. Im Folgenden die Registerbeschreibung eines  ATMega16, welcher über 8 ADC-Kanäle verfügt. Die Register unterscheiden sich jedoch nicht erheblich von denen anderer AVRs (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister A.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den &#039;&#039;&#039;ADC&#039;&#039;&#039; verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADEN&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able)&lt;br /&gt;
:Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren. Wenn das Bit nicht gesetzt ist, können die Pins wie normale I/O-Pins verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADSC&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion)&lt;br /&gt;
:Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.&lt;br /&gt;
:Wenn das Bit nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.&lt;br /&gt;
:Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADFR&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;F&#039;&#039;&#039;ree &#039;&#039;&#039;R&#039;&#039;&#039;un select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Ist das Bit auf 1 gesetzt arbeitet der ADC im Freerunning Modus. Dabe iwird das Datenregister permanent aktualisiert. Ist das Bit hingegen auf 0 gesetzt macht der ADC nur eine Single Conversion. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIF&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag)&lt;br /&gt;
:Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt, sobald eine Umwandlung erfolgt ist und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert wurde. Das Bit wird bei lesendem Zugriff auf &#039;&#039;&#039;ADC(L,H)&#039;&#039;&#039; automatisch (d.h. durch die Hardware) gelöscht.&lt;br /&gt;
:Wenn das &#039;&#039;&#039;ADIE&#039;&#039;&#039; Bit sowie das &#039;&#039;&#039;I-Bit&#039;&#039;&#039; im AVR &#039;&#039;&#039;Statusregister&#039;&#039;&#039; gesetzt ist, wird der &#039;&#039;&#039;ADC Interrupt&#039;&#039;&#039; ausgelöst und die Interrupt-Behandlungsroutine aufgerufen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn die Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 in das Register geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIE&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;, dann wird der &#039;&#039;&#039; ADC-Interrupt&#039;&#039;&#039; aktiviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADPS2...ADPS0&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;P&#039;&#039;&#039;rescaler &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
:Der &#039;&#039;&#039;ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz ergibt.&lt;br /&gt;
:Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
TF_{min}=\frac{CLK}{200\,\mathrm{kHz}}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50\,\mathrm{kHz}}=\frac{4000000}{50000}=\mathbf{80}&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im Interesse der schnelleren Wandlungszeit werden wir hier den Faktor 32 einstellen.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCL&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ADCH&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC &#039;&#039;&#039; Data Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn eine Umwandlung abgeschlossen ist, befindet sich der gemessene Wert in&lt;br /&gt;
diesen beiden Registern. Von &#039;&#039;&#039;ADCH&#039;&#039;&#039; werden nur die beiden niederwertigsten Bits verwendet. Es müssen immer beide Register ausgelesen werden, und zwar immer &#039;&#039;&#039;in der Reihenfolge: ADCL, ADCH&#039;&#039;&#039;. &lt;br /&gt;
Der effektive Messwert ergibt sich dann zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCL;       // mit uint16_t x&lt;br /&gt;
x += (ADCH&amp;lt;&amp;lt;8); // in zwei Zeilen (LSB/MSB-Reihenfolge und&lt;br /&gt;
                // C-Operatorpriorität sichergestellt)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADMUX&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Mu&#039;&#039;&#039;ltiple&#039;&#039;&#039;x&#039;&#039;&#039;er Select Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Register wird der zu messende Kanal ausgewählt. Beim 90S8535&lt;br /&gt;
kann jeder Pin von Port A als &#039;&#039;&#039;ADC&#039;&#039;&#039;-Eingang verwendet werden (=8 Kanäle).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADLAR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX4&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX3&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;REFS1...REFS0&#039;&#039;&#039; (&#039;&#039;&#039;Ref&#039;&#039;&#039;erence&#039;&#039;&#039;S&#039;&#039;&#039;election Bits)&lt;br /&gt;
:Mit diesen Bits kann die Referenzspannung eingestellt werden:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Referenzspanung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Externes AREF&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | AVCC als Referenz&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Interne 2,56 Volt&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADLAR&#039;&#039;&#039; (&#039;&#039;&#039;ADC &#039;&#039;&#039; &#039;&#039;&#039;L&#039;&#039;&#039;eft &#039;&#039;&#039;A&#039;&#039;&#039;djust &#039;&#039;&#039;R&#039;&#039;&#039;esult)&lt;br /&gt;
:Das ADLAR Bit verändert das Aussehen des Ergebnisses der AD-Wandlung. Bei einer logischen 1 wird das Ergebnis linksbündig ausgegeben, bei einer 0 rechtsündig. Eine Änderung in diesem Bit beeinflusst das Ergebnis sofort, ganz egal ob bereits eine Wandlung läuft.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX4...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 5 Bits wird der zu messende Kanal bestimmt. Wenn man einen einfachen 1-kanaligen ADC verwendet wird einfach die entsprechende Pinnummer des Ports in die Bits 0...2 eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
:Eine Empfehlung ist deswegen diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren des ADC ====&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039; ADC&#039;&#039;&#039; zu aktivieren, müssen wir das &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bit im &#039;&#039;&#039;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
Ein kleines Beispiel für den &amp;quot;single conversion&amp;quot;-Mode bei einem ATmega169 und Nutzung der internen Referenzspannung (beim &#039;169 1,1V bei anderen AVRs auch 2,56V). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler einstellen. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1023 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint16_t ReadChannel(uint8_t mux)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
&lt;br /&gt;
  ADMUX = mux;                      // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler &lt;br /&gt;
                               // setzen auf 8 (1) und ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  /* nach Aktivieren des ADC wird ein &amp;quot;Dummy-Readout&amp;quot; empfohlen, man liest&lt;br /&gt;
     also einen Wert und verwirft diesen, um den ADC &amp;quot;warmlaufen zu lassen&amp;quot; */&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
     ;     // auf Abschluss der Konvertierung warten &lt;br /&gt;
  }&lt;br /&gt;
  result = ADCW;  // ADCW muss einmal gelesen werden,&lt;br /&gt;
                  // sonst wird Ergebnis der nächsten Wandlung&lt;br /&gt;
                  // nicht übernommen.&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  result = 0; &lt;br /&gt;
  for( i=0; i&amp;lt;4; i++ )&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);            // eine Wandlung &amp;quot;single conversion&amp;quot;&lt;br /&gt;
    while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
      ;   // auf Abschluss der Konvertierung warten&lt;br /&gt;
    }&lt;br /&gt;
    result += ADCW;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Beispielaufrufe: */&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
&lt;br /&gt;
  adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -&amp;gt; Channel 0 */&lt;br /&gt;
  ...&lt;br /&gt;
  adcval = ReadChannel(2); /* MUX-Bits auf 0b0010 -&amp;gt; Channel 2 */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekennzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Das Löschen des ADIF-Flags sollte, &#039;&#039;&#039;entgegen&#039;&#039;&#039; der [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_intbits FAQ], mit&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADIF);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
erfolgen. Die Methode in der FAQ eignet sich nur für Register in denen &#039;&#039;&#039;nur&#039;&#039;&#039; Interrupt-Flags stehen.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Analog-Digital-Wandlung ohne internen ADC ===&lt;br /&gt;
&lt;br /&gt;
==== Messen eines Widerstandes ====&lt;br /&gt;
&lt;br /&gt;
Analoge Werte lassen sich ohne Analog-Digital-Wandler auch indirekt ermitteln. Im Folgenden wird die Messung des an einem Potentiometer eingestellten Widerstands anhand der Ladekurve eines Kondensators erläutert. Bei dieser Methode wird nur ein Portpin benötigt, ein Analog-Digital-Wandler oder Analog-Comparator ist nicht erforderlich. Es wird dazu ein Kondensator und der Widerstand (das Potentiometer) in Reihe zwischen Vorsorgungsspannung und Masse/GND geschaltet (sogen. RC-Netzwerk). Zusätzlich wird eine Verbindung der Leitung zwischen Kondensator und Potentiometer zu einem Portpin des Controllers hergestellt. Die folgende Abbildung verdeutlicht die erforderliche Schaltung. &lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wird der Portpin des Controllers auf Ausgang konfiguriert (im Beispiel &#039;&#039;DDRD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) und dieser Ausgang auf Logisch 1 (&amp;quot;High&amp;quot;, &#039;&#039;PORTD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) geschaltet, liegt an beiden &amp;quot;Platten&amp;quot; des Kondensators das gleiche Potential &#039;&#039;&#039;VCC&#039;&#039;&#039; an und der Kondensator somit entladen. (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so, da an beiden Seiten des Kondensators das gleiche Potential anliegt und somit eine Potentialdifferenz von 0V besteht =&amp;gt; Kondensator ist entladen).&lt;br /&gt;
&lt;br /&gt;
Nach einer gewissen Zeit ist der Kondensator entladen und der Portpin wird als Eingang konfiguriert (&#039;&#039;DDRD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2); PORTD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2)&#039;&#039;), wodurch dieser hochohmig wird. Der Status des Eingangspin (in PIND) ist Logisch 1 (High). Der Kondensator lädt sich jetzt über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), wird das Eingangssignal als LOW erkannt (Bit in PIND wird 0). Die Zeitspanne zwischen der Umschaltung von Entladung auf Aufladung und dem Wechsel des Eingangssignals von High auf Low ist ein Maß für den am Potentiometer eingestellten Widerstand. Zur Zeitmessung kann einer der im Controller vorhandenen Timer genutzt werden. Der 220 Ohm Widerstand dient dem Schutz des Controllers. Es würde sonst bei Maximaleinstellung des Potentionmeters (hier 0 Ohm) ein zu hoher Strom fließen, der die Ausgangsstufe des Controllers zerstört. &lt;br /&gt;
&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B: 0...100 % oder so) umzurechnen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Link 404 =&amp;gt; auskommentiert, mthomas 9.2.2008 &lt;br /&gt;
Ein Beispielprogramm findet sich auf [http://www.mypage.bluewin.ch/ch_schifferle/ Christian Schifferles Web-Seite] im Archiv &#039;&#039;ATMEL.ZIP&#039;&#039;, welches unter den Titel &#039;&#039;Tutorial &amp;quot;Programmieren mit C für Atmel Mikrocontroller&#039;&#039; heruntergeladen werden kann. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ADC über Komparator ====&lt;br /&gt;
&lt;br /&gt;
Es gibt einen weiteren Weg, eine analoge Spannung mit Hilfe des&lt;br /&gt;
Komparators, welcher in fast jedem AVR integriert ist, zu messen. Siehe dazu&lt;br /&gt;
auch die Application Note AVR400 von Atmel.&lt;br /&gt;
&lt;br /&gt;
Dabei wird das zu messende Signal auf den invertierenden Eingang&lt;br /&gt;
des Komparators geführt. Zusätzlich wird ein Referenzsignal an den nicht&lt;br /&gt;
invertierenden Eingang des Komparators angeschlossen. Das Referenzsignal wird&lt;br /&gt;
hier auch wieder über ein RC-Glied erzeugt, allerdings mit festen Werten für R&lt;br /&gt;
und C.&lt;br /&gt;
&lt;br /&gt;
[[Image:ADC ueber Komparator.gif]]&lt;br /&gt;
&lt;br /&gt;
Das Prinzip der Messung ist nun dem vorhergehenden recht&lt;br /&gt;
ähnlich. Durch Anlegen eines LOW-Pegels an Pin 2 wird der Kondensator zuerst&lt;br /&gt;
einmal entladen. Auch hier muss darauf geachtet werden, dass der Entladevorgang&lt;br /&gt;
genügend lang dauert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Nun wird Pin 2 auf HIGH gelegt. Der Kondensator wird geladen. Wenn die Spannung&lt;br /&gt;
über dem Kondensator die am Eingangspin anliegende Spannung erreicht hat&lt;br /&gt;
schaltet der Komparator durch. Die Zeit, welche benötigt wird, um den&lt;br /&gt;
Kondensator zu laden kann nun auch wieder als Maß für die Spannung an Pin 1&lt;br /&gt;
herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Ich habe es mir gespart, diese Schaltung auch aufzubauen und&lt;br /&gt;
zwar aus mehreren Gründen:&lt;br /&gt;
&lt;br /&gt;
# 3 Pins notwendig.&lt;br /&gt;
# Genauigkeit vergleichbar mit einfacherer Lösung.&lt;br /&gt;
# War einfach zu faul.&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung liegt allerdings darin, dass damit&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines Digital-Analog-Konverters (&#039;&#039;&#039;DAC&#039;&#039;&#039;) können wir nun auch Analogsignale ausgeben. Es gibt hier mehrere Verfahren. &amp;lt;!-- Wenn wir beim ADC die Möglichkeit haben, mit externen Komponenten zu operieren, müssen wir bei der DAC-Wandlung mit dem auskommen, was der Controller selber zu bieten hat. --mt: hmm, richtig? verstaendlich? redundant? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DAC über mehrere digitale Ausgänge ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir an den Ausgängen des Controllers ein entsprechendes&lt;br /&gt;
Widerstandsnetzwerk aufbauen haben wir die Möglichkeit, durch die Ansteuerung&lt;br /&gt;
der Ausgänge über den Widerständen einen Addierer aufzubauen, mit dessen&lt;br /&gt;
Hilfe wir eine dem Zahlenwert proportionale Spannung erzeugen können. Das&lt;br /&gt;
Schaltbild dazu kann etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
[[Image:DAC R2R.gif]]&lt;br /&gt;
&lt;br /&gt;
Es sollten selbstverständlich möglichst genaue Widerstände verwendet&lt;br /&gt;
werden, also nicht unbedingt solche mit einer Toleranz von 10% oder mehr.&lt;br /&gt;
Weiterhin empfiehlt es sich, je nach Anwendung den Ausgangsstrom über einen&lt;br /&gt;
Operationsverstärker zu verstärken.&lt;br /&gt;
&lt;br /&gt;
=== PWM (Pulsweitenmodulation) ===&lt;br /&gt;
&lt;br /&gt;
Wir kommen nun zu einem Thema, welches in aller Munde ist, aber viele&lt;br /&gt;
Anwender verstehen nicht ganz, wie &#039;&#039;&#039; PWM&#039;&#039;&#039; eigentlich funktioniert.&lt;br /&gt;
&lt;br /&gt;
Wie wir alle wissen, ist ein Mikrocontroller ein rein digitales Bauteil.&lt;br /&gt;
Definieren wir einen Pin als Ausgang, dann können wir diesen Ausgang entweder&lt;br /&gt;
auf HIGH setzen, worauf am Ausgang die Versorgungsspannung &#039;&#039;&#039; Vcc&#039;&#039;&#039; anliegt, oder aber wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt. Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen HIGH und LOW umschalten? - Richtig, wir erhalten eine Rechteckspannung, wie die folgende Abbildung zeigt:&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 1.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Rechteckspannung hat nun einen arithmetischen Mittelwert, &lt;br /&gt;
der je nach Pulsbreite kleiner oder größer ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 2.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun diese pulsierende Ausgangsspannung noch über ein RC-Glied filtern/&amp;quot;glätten&amp;quot;, dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVRs können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. &lt;br /&gt;
Dazu dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt, weshalb ein Blick ins entsprechende Datenblatt dringend angeraten wird.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren, müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| PWM-Modus des Timers ist nicht aktiv.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Timer/Counter zählt nun permanent von 0 bis zur Obergrenze&lt;br /&gt;
und wieder zurück, er wird also als sogenannter Auf-/Ab Zähler betrieben. &lt;br /&gt;
Die Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss mit den Bits &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; desselben&lt;br /&gt;
Registers die gewünschte Ausgabeart des Signals definiert werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Nicht invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Hochzählen und gesetzt beim&lt;br /&gt;
Herunterzählen.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Herunterzählen und gesetzt beim&lt;br /&gt;
Hochzählen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl, um beispielsweise den Timer/Counter als&lt;br /&gt;
nicht invertierenden 10-Bit PWM zu verwenden, heißt dann:&lt;br /&gt;
&lt;br /&gt;
alte Schreibweise (PWMxx wird nicht mehr akzeptiert)&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;PWM11)|(1&amp;lt;&amp;lt;PWM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
neue Schreibweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;WGM11)|(1&amp;lt;&amp;lt;WGM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Timer/Counter überhaupt läuft, müssen wir im Control&lt;br /&gt;
Register B &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; noch den gewünschten Takt (Vorteiler) einstellen und&lt;br /&gt;
somit auch die Frequenz des &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signals bestimmen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stop. Der Timer/Counter wird gestoppt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin 1, negative Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin 1, positive Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Also um einen Takt von CK / 1024 zu generieren, verwenden wir&lt;br /&gt;
folgenden Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1B = (1&amp;lt;&amp;lt;CS12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt muss nur noch der Vergleichswert festgelegt werden. Diesen&lt;br /&gt;
schreiben wir in das 16-Bit Timer/Counter Output Compare Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem Vergleichswert und dem generierten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal aufzeigen.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
Ach ja, fast hätte ich&#039;s vergessen. Das generierte &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal&lt;br /&gt;
wird am Output Compare Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; des Timers ausgegeben und leider können wir&lt;br /&gt;
deshalb auch beim AT90S2313 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren. Andere AVR-Typen verfügen über bis zu vier PWM-Ausgänge. Zu beachten ist außerdem, das wenn der OC Pin aktiviert ist, er nichtmehr wie üblich funktioniert und z.B. nicht einfach über PINx ausgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Ein Programm, dass an einem ATMega8 den Fast-PWM Modus verwendet, den Modus 14, könnte so aussehen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // OC1A auf Ausgang&lt;br /&gt;
  DDRB = (1 &amp;lt;&amp;lt; PB1 );&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // Timer 1 einstellen&lt;br /&gt;
  //  &lt;br /&gt;
  // Modus 14:&lt;br /&gt;
  //    Fast PWM, Top von ICR1&lt;br /&gt;
  //&lt;br /&gt;
  //     WGM13    WGM12   WGM11    WGM10&lt;br /&gt;
  //      1        1       1        0&lt;br /&gt;
  //&lt;br /&gt;
  //    Timer Vorteiler: 1&lt;br /&gt;
  //     CS12     CS11    CS10&lt;br /&gt;
  //       0        0       1&lt;br /&gt;
  //&lt;br /&gt;
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match&lt;br /&gt;
  //     COM1A1   COM1A0&lt;br /&gt;
  //       1        0&lt;br /&gt;
 &lt;br /&gt;
  TCCR1A = (1&amp;lt;&amp;lt;COM1A1) | (1&amp;lt;&amp;lt;WGM11);&lt;br /&gt;
  TCCR1B = (1&amp;lt;&amp;lt;WGM13) | (1&amp;lt;&amp;lt;WGM12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  //  den Endwert (TOP) für den Zähler setzen&lt;br /&gt;
  //  der Zähler zählt bis zu diesem Wert&lt;br /&gt;
&lt;br /&gt;
  ICR1 = 0x6FFF;&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  // der Compare Wert&lt;br /&gt;
  // Wenn der Zähler diesen Wert erreicht, wird mit&lt;br /&gt;
  // obiger Konfiguration der OC1A Ausgang abgeschaltet&lt;br /&gt;
  // Sobald der Zähler wieder bei 0 startet, wird der&lt;br /&gt;
  // Ausgang wieder auf 1 gesetzt&lt;br /&gt;
  //&lt;br /&gt;
  // Durch Verändern dieses Wertes, werden die unterschiedlichen&lt;br /&gt;
  // PWM Werte eingestellt.&lt;br /&gt;
&lt;br /&gt;
  OCR1A = 0x3FFF;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  while( 1 )&lt;br /&gt;
    ;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für Details der PWM Möglichkeiten, muß immer das jeweilge Datenblatt des Prozessors konsultiert werden, da sich die unterschiedlichen Prozessoren in ihren Möglichkeiten doch stark unterscheiden. Auch muß man aufpassen, welches zu setzende Bit in welchem Register sind. Auch hier kann es sein, dass gleichnamige Konfigurationsbits in unterschiedlichen Konfigurationsregistern (je nach konkretem Prozessortyp) sitzen.&lt;br /&gt;
&lt;br /&gt;
= LCD-Ansteuerung =&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung an der LCD-Controller-Platine ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; (ist in manchen Unterlagen auch &#039;&#039;&#039;EN&#039;&#039;&#039;  für &#039;&#039;Enable&#039;&#039; abgekürzt) benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert, ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitetet hat (diese Methode u.a. in der LCD-Library von Peter Fleury verwendet). Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt da wir wissen, welche Anschlüsse das LCDs benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin #-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin-µC&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND oder Poti (siehe oben)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD4 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD5 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD0 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD1 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD2 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD3 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man die Steuerleitungen EN und RS auf Pins an einem anderen Port legen möchte, kann man so wie in diesem [http://www.mikrocontroller.net/topic/88543#751982 Forumsbeitrag] vorgehen.&lt;br /&gt;
&lt;br /&gt;
Ok, alles ist verbunden, wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display? &lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
void lcd_data(unsigned char temp1);&lt;br /&gt;
void lcd_string(char *data);&lt;br /&gt;
void lcd_command(unsigned char temp1);&lt;br /&gt;
void lcd_enable(void);&lt;br /&gt;
void lcd_init(void);&lt;br /&gt;
void lcd_home(void);&lt;br /&gt;
void lcd_clear(void);&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y);&lt;br /&gt;
&lt;br /&gt;
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
&lt;br /&gt;
// LCD Befehle&lt;br /&gt;
&lt;br /&gt;
#define CLEAR_DISPLAY 0x01&lt;br /&gt;
#define CURSOR_HOME   0x02&lt;br /&gt;
&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
&lt;br /&gt;
#define LCD_PORT      PORTD&lt;br /&gt;
#define LCD_DDR       DDRD&lt;br /&gt;
#define LCD_RS        PD4&lt;br /&gt;
#define LCD_EN        PD5&lt;br /&gt;
// DB4 bis DB7 des LCD sind mit PD0 bis PD3 des AVR verbunden&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// sendet ein Datenbyte an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_data(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_RS);        // RS auf 1 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_command(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);        // RS auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;              // oberes Nibble holen&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;            // maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;            // unteres Nibble holen und maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// erzeugt den Enable-Puls&lt;br /&gt;
void lcd_enable(void)&lt;br /&gt;
{&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
    _delay_us(1);                   // kurze Pause&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/80900&lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Initialisierung: &lt;br /&gt;
// Muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
void lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
   LCD_DDR = LCD_DDR | 0x0F | (1&amp;lt;&amp;lt;LCD_RS) | (1&amp;lt;&amp;lt;LCD_EN);   // Port auf Ausgang schalten&lt;br /&gt;
&lt;br /&gt;
   // muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(15);&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x03;            &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);      // RS auf 0&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4 Bit Modus aktivieren &lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x02;&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4Bit / 2 Zeilen / 5x7&lt;br /&gt;
   lcd_command(0x28);&lt;br /&gt;
    &lt;br /&gt;
   // Display ein / Cursor aus / kein Blinken&lt;br /&gt;
   lcd_command(0x0C); &lt;br /&gt;
 &lt;br /&gt;
   // inkrement / kein Scrollen&lt;br /&gt;
   lcd_command(0x06);&lt;br /&gt;
&lt;br /&gt;
   lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
&lt;br /&gt;
void lcd_clear(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CLEAR_DISPLAY);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
&lt;br /&gt;
void lcd_home(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CURSOR_HOME);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)&lt;br /&gt;
&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t tmp;&lt;br /&gt;
&lt;br /&gt;
  switch (y) {&lt;br /&gt;
    case 1: tmp=0x80+0x00+x; break;    // 1. Zeile&lt;br /&gt;
    case 2: tmp=0x80+0x40+x; break;    // 2. Zeile&lt;br /&gt;
    case 3: tmp=0x80+0x10+x; break;    // 3. Zeile&lt;br /&gt;
    case 4: tmp=0x80+0x50+x; break;    // 4. Zeile&lt;br /&gt;
  }&lt;br /&gt;
  lcd_command(tmp);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_string(char *data)&lt;br /&gt;
{&lt;br /&gt;
    while(*data) {&lt;br /&gt;
        lcd_data(*data);&lt;br /&gt;
        data++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches die Funktionen benutzt, sieht zb. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen&lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    lcd_data(&#039;T&#039;);&lt;br /&gt;
    lcd_data(&#039;e&#039;);&lt;br /&gt;
    lcd_data(&#039;s&#039;);&lt;br /&gt;
    lcd_data(&#039;t&#039;);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
&lt;br /&gt;
    lcd_string(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist dabei, dass die Optimierung bei der Compilierung eingeschaltet ist, sonst stimmen die Zeiten der Funktionen _delay_us() und _delay_ms() nicht und der Code wird wesentlich länger (Siehe Dokumentation der libc im WinAVR).&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches eine Variable ausgibt, sieht zb. so aus.&lt;br /&gt;
Mittels der itoa() Funktion (itoa = &amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;nteger &amp;lt;b&amp;gt;To&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;A&amp;lt;/b&amp;gt;scii ) wird von einem Zahlenwert eine textuelle Repräsentierung ermittelt (sprich: ein String erzeugt) und dieser String mit der bereits vorhandenen Funktion lcd_string ausgegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen &lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Beispiel&lt;br /&gt;
int variable = 42;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe des Zeichens dessen ASCII-Code gleich dem Variablenwert ist&lt;br /&gt;
    // (Im Beispiel entspricht der ASCII-Code 42 dem Zeichen *)&lt;br /&gt;
    // http://www.code-knacker.de/ascii.htm&lt;br /&gt;
    lcd_data(variable);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
 &lt;br /&gt;
    // Ausgabe der Variable als Text in dezimaler Schreibweise&lt;br /&gt;
    {&lt;br /&gt;
       // ... umwandeln siehe FAQ Artikel bei www.mikrocontroller.net&lt;br /&gt;
       // WinAVR hat eine itoa()-Funktion, das erfordert obiges #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
       char Buffer[20]; // in diesem {} lokal&lt;br /&gt;
       itoa( variable, Buffer, 10 ); &lt;br /&gt;
&lt;br /&gt;
       // ... ausgeben  &lt;br /&gt;
       lcd_string( Buffer );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einrichten eines Projekts muss man zu der Datei mit dem Hauptprogramm auch die Datei lcd-routines.c in das Projekt aufnehmen. Dies geschieht beim AVR Studio unter Source Files im Fenster AVR GCC oder bei WinAVR im Makefile (z.B. durch SRC += lcd-routines.c).&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVRs sind für viele Steuerungsaufgaben zu schnell. Wenn wir beispielsweise eine LED oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die CPU-Frequenz verwenden, da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, Vorgänge in Zeitabständen durchzuführen, die geringer als die Taktfrequenz des Controllers sind. Selbstverständlich sollte die resultierende Frequenz auch noch möglichst genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist ganz einfach ein bestimmtes Register im µC, das völlig ohne Zutun des Programms, also per Hardware, hochgezählt wird. Das alleine wäre noch nicht allzu nützlich, wenn nicht dieses Hardwareregister bei bestimmten Zählerständen einen Interrupt auslösen könnte. Ein solches Ereignis ist der Overflow: Da die Bitbreite des Registers beschränkt ist, kommt es natürlich auch vor, dass der Zähler so hoch zählt, dass der nächste Zählerstand mit dieser Bitbreite nicht mehr darstellbar ist und der Zähler wieder auf 0 zurückgesetzt wird. Dieses Ereignis nennt man den Overflow und es ist möglich an dieses Ereignis einen Interrupt zu koppeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Alternativvorschlag mthomas &lt;br /&gt;
Jeder Timer verfügt über ein Zählerregister im Mikrocontroller, das automatisch und ohne Zutun des Programms von der Hardware weitergezählt wird. In einem einfachen Anwendungsfall stellt man den Timer auf eine Zählgeschwindigkeit (Frequenz) und kann dann anhand des Zählerstands ermitteln, wie viel Zeit vergangen ist. Das eigentlich Nützliche an Timern ist jedoch, dass man  bestimmte Zählerstände mit Interrupts verknüpfen kann, so dass der Controller beim Auftreten automatisch eine vom Anwender geschriebene Routine aufruft. Eines dieser möglichen Ereignis ist der Overflow ((Zähler-)Überlauf), der dann auftritt, wenn der Wert des Zählerregisters (Timer/Counter-Register) den maximal möglichen Wert überschreitet. Der Maximalwert wird durch die Bitbreite des Zählerregisters bestimmt (z.B. 255 bei 8-Bit Timern). Beim Überlauf/Overflow wird der Zähler durch die Hardware auf 0 zurückgesetzt und die Zählung beginnt von neuem. Wurde vorher der Overflow-Interrupt für den Timer aktiviert (im Timer Control Register) unterbricht der Controller automatisch die Ausführung des Hauptprogramms und verzweigt in die Interrupt-Routine des Anwenders.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auf den AT90S2313. Für andere Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer Auflösung von 65536. Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz, der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
== Der Vorteiler (Prescaler) ==&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler dient dazu, den CPU-Takt vorerst um einen einstellbaren Faktor zu reduzieren. Die so geteilte Frequenz wird den Eingängen der Timer zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024 einstellen, wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca. 4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw. Zählregister (TCNTx) mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle verfügen über mindestens einen, teilweise sogar zwei, 8-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird z.B bei AT90S2313 über folgende Register angesprochen (bei anderen Typen weitestgehend analog):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    TCCR0 |= (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst und die daran gebundene Interrupt-Routine abgearbeitet. Das TOV Flag &lt;br /&gt;
lässt sich durch das Hineinschreiben einer 1 und nicht wie erwartet einer 0 wieder zurücksetzen.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen außer den 8-Bit Timern auch 16-Bit Timer. Die 16-Bit Timer/Counter sind etwas komplexer aufgebaut als die 8-Bit Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM13&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM12 (CTC1)&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorteiler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt, und C der voreingestellte Vergleichswert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin T1, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin T1, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T1 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T1 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0, bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;TCNT1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;OCR1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;OCR1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;OCR1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;OCR1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäß folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]] [[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Ausserdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr. Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen kleiner 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4us warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.6 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t x;&lt;br /&gt;
&lt;br /&gt;
    x = 10;&lt;br /&gt;
&lt;br /&gt;
    while (x &amp;gt;= 0)&lt;br /&gt;
    {&lt;br /&gt;
      // tu was&lt;br /&gt;
&lt;br /&gt;
      x--;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog? ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Einige Controller haben einen eigenen Watchdog Oszillator, z.B. der Tiny2313 mit 128kHz. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde, beginnt der Counter von 0 an hochzuzählen. &lt;br /&gt;
Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern, müssen wir den Watchdog regelmäßig wieder neu starten bzw. rücksetzen (Watchdog Reset). &lt;br /&gt;
Dies sollte innerhalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern, muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten. Es müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. &lt;br /&gt;
Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist, wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei &#039;&#039;wdt.h&#039;&#039; (&#039;&#039;#include &amp;lt;avr/wdt.h&amp;gt;&#039;&#039;) in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch gestartet wird. &lt;br /&gt;
Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. &lt;br /&gt;
Falls ein anderer Wert gewünscht ist, so kann dies im Makfile in den Linker-Optionen eingetragen werden. &lt;br /&gt;
Dazu muss in der Zeile LDFLAGS folgende Option angefügt werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert&lt;br /&gt;
:Mögliche Timeoutwerte:&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Konstante&lt;br /&gt;
! Wert&lt;br /&gt;
! TimeOut&lt;br /&gt;
|- &lt;br /&gt;
| WDTO_15MS   &lt;br /&gt;
| 0&lt;br /&gt;
| 15 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_30MS   &lt;br /&gt;
| 1&lt;br /&gt;
| 30 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_60MS   &lt;br /&gt;
| 2&lt;br /&gt;
| 60 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_120MS   &lt;br /&gt;
| 3&lt;br /&gt;
| 120 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_250MS   &lt;br /&gt;
| 4&lt;br /&gt;
| 250 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_500MS   &lt;br /&gt;
| 5&lt;br /&gt;
| 500 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_1S   &lt;br /&gt;
| 6&lt;br /&gt;
| 1 S&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_2S   &lt;br /&gt;
| 7&lt;br /&gt;
| 2s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
== Watchdog-Anwendungshinweise ==&lt;br /&gt;
&lt;br /&gt;
Ob nun der Watchdog als Schutzfunktion überhaupt verwendet werden soll, hängt stark von der Anwendung, der genutzten Peripherie und dem Umfang und der Qualitätssicherung des Codes ab. Will man sicher gehen, dass ein Programm sich nicht in einer Endlosschleife verfängt, ist der Wachdog das geeignete Mittel dies zu verhindern. Weiterhin kann bei geschickter Programmierung der Watchdog dazu genutzt werden, bestimmte Stromsparfunktionen zu implementieren. Bei einigen neueren AVRs (z.B. dem ATTiny13) kann der Watchdog auch direkt als Timer genutzt werden, der den Controller aus einem Schlafmodus aufweckt. Auch dies kann im &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register eingestellt werden. Außerdem bietet der WD die einzige Möglichkeit einen beabsichtigten System-Reset (ein &amp;quot;richtiger Reset&amp;quot;, kein &amp;quot;jmp 0x0000&amp;quot;) ohne externe Beschaltung auszulösen, was z.B. bei der Implementierung eines Bootloaders nützlich ist. Bei bestimmten Anwendungen kann die Nutzung des WD als &amp;quot;ultimative Deadlock-Sicherung für nicht bedachte Zustände&amp;quot; natürlich immer als zusätzliche Sicherung dienen. &lt;br /&gt;
&lt;br /&gt;
Es besteht die Möglichkeit herauszufinden, ob ein Reset durch den Watchdog ausgelöst wurde (beim ATmega16 z.B. Bit WDRF in MCUCSR). Diese Information sollte auch genutzt werden, falls ein WD-Reset in der Anwendung nicht planmäßig implementiert wurde. Zum Beispiel kann man eine LED an einen freien Pin hängen, die nur bei einem Reset durch den WD aufleuchtet oder aber das &amp;quot;Ereignis WD-Reset&amp;quot; im internen EEPROM des AVR absichern, um die Information später z.B. über UART oder ein Display auszugeben (oder einfach den EEPROM-Inhalt über die ISP/JTAG-Schnittstelle auslesen).&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/77273#642501 Bug in ATtiny2313?]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt der Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft. Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe Abschnitt (TODO: verlinken) im Anhang.&lt;br /&gt;
&amp;lt;!--Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. vgl. [[AVR-GCC-Tutorial#Anhang|Anhang]])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.B. &#039;&#039;void TIMER0_OVF_vect(void)...&#039;&#039;). Der Unterschied im Vergleich zu ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben.  &amp;lt;!--Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)), als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur &lt;br /&gt;
in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechnungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechnungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0, PORTA ist danach 0b00000001. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-oder-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8 (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten &#039;&#039;Sleep-Modes&#039;&#039; (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_enable()&#039;&#039;&#039;&lt;br /&gt;
:aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_cpu()&#039;&#039;&#039;&lt;br /&gt;
: Versetzt den Controller in den Schlafmodus (sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt)&lt;br /&gt;
* &#039;&#039;&#039;sleep_disable()&#039;&#039;&#039;&lt;br /&gt;
:deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts.&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;allozieren&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich, um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory, bei AVR Controllern mit mehr als 64kB Flash auch elpm). Die Standard-Laufzeitbibliothek des avr-gcc (die avr-libc) stellt diese Funktionen nach Einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Lokale Variablen (eigentlich Konstanten) innerhalb von Funktionen können ebenfalls im Programmspeicher abgelegt werden. Dazu ist bei der Definition jedoch ein &#039;&#039;static&#039;&#039; voranzustellen, da solche &amp;quot;Variablen&amp;quot; nicht auf dem Stack bzw. (bei Optimierung) in Registern verwaltet werden können. Der Compiler &amp;quot;wirft&amp;quot; eine Warnung falls static fehlt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray2 };&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  static /*const*/ uint8_t pgmTestByteLocal PROGMEM = 0x55;&lt;br /&gt;
  static /*const*/ char pgmTestStringLocal[] PROGMEM = &amp;quot;im Flash&amp;quot;;&lt;br /&gt;
  // so nicht (static fehlt): char pgmTestStringLocalFalsch [] PROGMEM = &amp;quot;so nicht&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 // ...&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
    //...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte....&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWort);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;groß&amp;quot; (Stand avr-gcc 3.4.x). Damit ist der mögliche Speicherbereich für &amp;quot;Flash-Konstanten&amp;quot; auf 64kB begrenzt.  &amp;lt;!-- Einige avr-libc/pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher) (intern via Assembler Anweisung ELPM). Die Initialisierungswerde des Speicherinhalts jenseits der 64kB-Marke müssen dann jedoch auf anderem Weg angelegt werden (nicht PROGMEM, evtl. eigene Section und Linker-Optionen - TODO) /// alt - und nicht ganz korrekt: (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.)--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray enthält nun die Startadresse des Byte-Arrays pgmFooByteArray1&lt;br /&gt;
    // Allerdings würde ein direkter Zugriff mit diesem Pointer (z.B. temp=*ptrToArray)&lt;br /&gt;
    // &#039;&#039;&#039;nicht&#039;&#039;&#039; den Inhalt von pgmFooByteArray1[0] liefern, sondern von einer Speicherstelle&lt;br /&gt;
    // im &#039;&#039;&#039;RAM&#039;&#039;&#039;, die die gleiche Adresse hat wie pgmFooByteArray1[0]&lt;br /&gt;
    // Daher muss nun die Funktion pgm_read_byte() benutzt werden, die die in ptrToArray&lt;br /&gt;
    // enthaltene Adresse benutzt und auf das Flash zugreift.&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray enthält nun die Adresse des ersten Elements des Byte-Arrays pgmFooByteArray2&lt;br /&gt;
    // da im zweiten Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmFooByteArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Array aus Zeichenketten im Flash-Speicher ===&lt;br /&gt;
&lt;br /&gt;
Felder aus Zeichenketten im Flash-Speicher werden in zwei Schritten angelegt: Zuerst die einzelnen Elemente des Arrays und im Anschluss ein Array, in dem die Addressen der Zeichenketten abgelegt werden. Zum Auslesen wird zuerst die Adresse des i-ten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, auf das Element (die Zeichenkette) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const char str1[] PROGMEM = &amp;quot;first_A&amp;quot;;&lt;br /&gt;
const char str2[] PROGMEM = &amp;quot;second_A&amp;quot;;&lt;br /&gt;
const char str3[] PROGMEM = &amp;quot;third_A&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char *strarray1[] PROGMEM = {&lt;br /&gt;
	str1,&lt;br /&gt;
	str2,&lt;br /&gt;
	str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	int i, j, l;&lt;br /&gt;
	const char *pstrflash;&lt;br /&gt;
	char work[20], work2[20];&lt;br /&gt;
	// fuer Simulation: per volatile Optimierung verhindern, &lt;br /&gt;
	//                  da c nicht genutzt&lt;br /&gt;
	volatile char c;&lt;br /&gt;
	&lt;br /&gt;
	for ( i = 0; i &amp;lt; (sizeof(strarray1)/sizeof(strarray1[0]) ); i++ ) {&lt;br /&gt;
&lt;br /&gt;
		// setze Pointer auf die Addresse des i-ten Elements des&lt;br /&gt;
		// &amp;quot;Flash-Arrays&amp;quot; (str1, str2, ...)&lt;br /&gt;
		pstrflash = (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) );&lt;br /&gt;
		&lt;br /&gt;
		// kopiere den Inhalt der Zeichenkette von der&lt;br /&gt;
		// in pstrflash abgelegten Adresse in das work-Array&lt;br /&gt;
		// analog zu strcpy( work, strarray1[i]) wenn alles im RAM&lt;br /&gt;
		strcpy_P( work, pstrflash );&lt;br /&gt;
		// verkuerzt:&lt;br /&gt;
		strcpy_P( work2, (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) ) );&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
		// Zeichen-fuer-Zeichen&lt;br /&gt;
		l = strlen_P( pstrflash );&lt;br /&gt;
		for ( j=0; j &amp;lt; l; j++ ) {&lt;br /&gt;
			// analog zu c=strarray[i][j] wenn alles im RAM&lt;br /&gt;
			c = (char)( pgm_read_byte( pstrflash++ ) );&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	while (1) { ; }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: &amp;quot;How do I put an array of strings completely in ROM?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5)) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Vorsicht: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char textImFlashOK[] PROGMEM = &amp;quot;mit[]&amp;quot;; &lt;br /&gt;
// = Daten im &amp;quot;Flash&amp;quot;, textImFlashOK* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char* textImFlashProblem PROGMEM = &amp;quot;mit*&amp;quot;;&lt;br /&gt;
// Konflikt: Daten im BSS (lies: RAM), textImFlashFAIL* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der Initialisierungsstring von &amp;quot;textImFlashProblem&amp;quot; zu den Konstanten ans Ende des Programmcodes gelegt wird (BSS), von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte (und wird). Da der lesende Code (mittels pgm_read*) trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein weiters Problem des AVR-GCC (gesehen bei avr-gcc 3.4.1 und 3.4.2) bei der Anpassung an die Harvard-Architektur zu sein (konstanter Pointer auf variable Daten?!). Abhilfe (&amp;quot;Workaround&amp;quot;): Initialisierung bei Zeichenketten mit [] oder gleich im Code PSTR(&amp;quot;...&amp;quot;) nutzen.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden, ob es sich um eine Adresse im Flash oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind.&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die einen im Flash abgelegten String z.B. an eine UART ausgibt, würde dann so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void uart_puts_p(const char *text)&lt;br /&gt;
{&lt;br /&gt;
    char Zeichen;&lt;br /&gt;
&lt;br /&gt;
    while (Zeichen = pgm_read_byte(text))&lt;br /&gt;
    {   /* so lange, wie mittels pgm_read_byte ein Zeichen vom Flash gelesen&lt;br /&gt;
           werden konnte, welches nicht das &amp;quot;String-Endezeichen&amp;quot; darstellt */&lt;br /&gt;
&lt;br /&gt;
        /* Das gelesene Zeichen über die normalen Kanäle verschicken */&lt;br /&gt;
        uart_putc(Zeichen);&lt;br /&gt;
        text++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Flash in der Anwendung schreiben ===&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option (auch bekannt als Bootloader-Support) können Teile des Flash-Speichers auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. Bei wenigen &amp;quot;kleinen&amp;quot; AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm(Flash)- und Datenspeicher(RAM) auf. Der C-Standard und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiß&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, dass vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgegeben sind, die innerhalb weniger Taktzyklen aufeinanderfolgen müssen (&amp;quot;timed sequence&amp;quot;). Auch dies muss bei Nutzung der Funktionen aus der avr-libc/eeprom.h-Datei nicht selbst implementiert werden. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Siehe dazu folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt; // wird in aktuellen Versionen der avr-lib mit xx.h eingebunden&lt;br /&gt;
&lt;br /&gt;
// EEMEM wird bei aktuellen Versionen der avr-lib in eeprom.h definiert&lt;br /&gt;
// hier: definiere falls noch nicht bekannt (&amp;quot;alte&amp;quot; avr-libc)&lt;br /&gt;
#ifndef EEMEM&lt;br /&gt;
// alle Textstellen EEMEM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEMEM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
//...&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // &#039;Variablen&#039; eeFooByte geschrieben&lt;br /&gt;
//...&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z.B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    #define EEPROM_DEF 0xFF&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ( ( myByte = eeprom_read_byte(&amp;amp;eeFooByte) ) == EEPROM_DEF ) {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
//...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprüft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und möglicherweise noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,3);&lt;br /&gt;
&lt;br /&gt;
    /* dito etwas anschaulicher aber &amp;quot;unnütze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fließkommazahlen lassen sich recht praktisch über eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t i[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    uint8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEMEM;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
//...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklaration abgeleiteten EEPROM-Inhalt in eine Datei geschrieben werden (übliche Dateiendung: .eep, Daten im Intel Hex-Format). Damit können recht elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen (siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles). Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden (Write EEPROM), wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse (vgl. Datenblatt Abschnitt Fuse Bits) die vorherigen Daten (EESAVE programmed = 0), deren Position möglicherweise nicht mehr mit der Belegung im aktuellen Programm übereinstimmt oder den Standardwert nach &amp;quot;Chip Erase&amp;quot;: 0xFF (EESAVE unprogrammed = 1). Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Eine besondere Funktion des avr-gcc ist, dass mit entsprechenden Optionen im Makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Variable auf feste Adressen legen ===&lt;br /&gt;
&lt;br /&gt;
Gleich zu Beginn möchte ich darauf hinweisen, dass dieses Verfahren nur ein Workaround ist, mit dem man das Problem der anscheinend &amp;quot;zufälligen&amp;quot; Verteilung&lt;br /&gt;
der EEPROM-Variablen durch den Compiler etwas in den Griff bekommen kann.&lt;br /&gt;
&lt;br /&gt;
Hilfreich kann dies vor allem dann sein, wenn man z.B. über einen Kommandointerpreter (o.ä. Funktionen) direkt bestimmte EEPROM-Adressen manipulieren möchte. Auch wenn man über einen JTAG-Adapter (mk I oder mkII) den Programmablauf manipulieren möchte, indem man die EEPROM-Werte direkt ändert, kann diese Technik hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Im folgenden nun zwei Sourcelistings mit einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.h&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#inlcude &amp;lt;avr/eeprom.h&amp;gt;     // Die EEPROM-Definitionen/Macros der avr-libc einbinden&lt;br /&gt;
&lt;br /&gt;
#define   EESIZE   512      // Maximale Größe des EEPROMS&lt;br /&gt;
&lt;br /&gt;
#define   EE_DUMMY   0x000  // Dummyelement (Adresse 0 sollte nicht genutzt werden)&lt;br /&gt;
#define   EE_VALUE1  0x001  // Eine Bytevariable  &lt;br /&gt;
#define   EE_WORD1L  0x002  // Eine Wordvariable (Lowbyte)&lt;br /&gt;
#define   EE_WORD1H  0x003  // Eine Wordvariable (Highbyte)&lt;br /&gt;
#define   EE_VALUE2  0x004  // Eine weitere Bytevariable&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den Macros &#039;&#039;&#039;#define EE_VALUE1&#039;&#039;&#039; legt man den Namen und die Adresse der&lt;br /&gt;
&#039;Variablen&#039; fest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Die Adressen sollten fortlaufend, zumindest aber aufsteigend sortiert sein! Ansonsten besteht die Gefahr, daß man sehr schnell ein Durcheinander im EEPROM Speicher veranstaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Für den Compiler sind das lediglich Speicher-Adressen, über die auf das EEPROM zugegriffen wird. Der Compiler sieht nichts davon als eine echte Variable an und stößt sich daher auch nicht daran, wenn 2 Makros mit der gleichen Speicheradresse, bzw. überlappenden Speicherbereichen definiert werden. Es liegt einzig und alleine in der Hand des Programmierers, hier keinen Fehler zu machen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
   [EE_DUMMY]   = 0x00,&lt;br /&gt;
   [EE_VALUE1]  = 0x05,&lt;br /&gt;
   [EE_WORD1L]  = 0x01,   &lt;br /&gt;
   [EE_WORD1H]  = 0x00,&lt;br /&gt;
   [EE_VALUE2]  = 0xFF&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch die Verwendung eines Array, welches das gesammte EEPROM umfasst, bleibt&lt;br /&gt;
dem Compiler nicht anderes übrig, als das Array so zu platzieren, dass Element 0&lt;br /&gt;
des Arrays der Adresse 0 des EEPROMs entspricht. (&#039;&#039;Ich hoffe nur, dass die Compilerbauer daran nichts ändern!&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Wie man in dem obigen Codelisting auch sehen kann, hat das Verfahren einen kleinen Haken. Variablen die größer sind als 1 Byte, müssen etwas umständlicher&lt;br /&gt;
definiert werden. Benötigt man keine Initialisierung durch das Programm (was der Normalfall sein dürfte), dann kann man das auch so machen:&lt;br /&gt;
&lt;br /&gt;
Möchte man im EEPROM hintereinander beispielsweise Variablen, mit den Namen &#039;&#039;&#039;Wert&#039;&#039;&#039;, &#039;&#039;&#039;Anzahl&#039;&#039;&#039;, &#039;&#039;&#039;Name&#039;&#039;&#039; und &#039;&#039;&#039;Wertigkeit&#039;&#039;&#039; definieren, wobei Wert und Wertigkeit 1 Byte belegen sollen, Anzahl als 1 Wort (also 2 Bytes) und Name mit 10 Bytes reserviert werden soll, so geht auch folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes Makro definiert also seine Startadresse durch die Startadresse der unmittelbar vorhergehende &#039;Variablen&#039; plus der Anzahl der Bytes die von der vorhergehenden &#039;Variablen&#039; verbraucht werden. Dadurch ist man zumindest etwas auf der sicheren Seite, dass keine 2 &#039;Variablen&#039; im EEPROM überlappend definiert werden. Möchte man eine weitere &#039;Variable&#039; hinzufügen, so wird deren&lt;br /&gt;
Name, einfach anstelle der EE_LAST eingesetzt und eine neue Zeile für EE_LAST eingefügt, in der dann die Größe der &#039;Variablen&#039; festgelegt wird. Zb.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_PROZENT    ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( double ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EE_PROZENT legt die Startadresse für eine neue &#039;Variable&#039; des Datentyps double fest.&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf die EEPROM Werte kann dann z.B.so erfolgen:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t   temp1;&lt;br /&gt;
uint16_t  temp2;&lt;br /&gt;
&lt;br /&gt;
temp1 = eeprom_read_byte(EE_VALUE1);&lt;br /&gt;
temp2 = eeprom_read_word(EE_WORD1L);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ob die in der avr-libc vorhandenen Funktionen dafür verwendet werden können, weiß ich nicht. Aber in einigen Fällen muss man sich sowieso eigene Funktionen&lt;br /&gt;
bauen, welche die spezifischen Anforderungen (Interrupt - Atom Problem, etc.)&lt;br /&gt;
erfüllen.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist nur eine Möglichkeit, wie man dies realisieren kann. Sie bietet einem eine relativ einfache Art die EEPROM-Werte&lt;br /&gt;
auf beliebige Adressen zu legen oder Adressen zu ändern. Die Andere Möglichkeit besteht darin, die EEPROM-Werte wie folgt zu belegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
  0x00,                     //  ee_dummy&lt;br /&gt;
  0x05,                     //  ee_value1&lt;br /&gt;
  0x01,                     //  ee_word1L&lt;br /&gt;
  0x00,                     // (ee_word1H)&lt;br /&gt;
  0xFF                      //  ee_value2&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei kann man Variablen, die größer sind als 1 Byte einfacher definieren und&lt;br /&gt;
man muss nur die Highbyte- oder Lowbyte-Adresse in der &amp;quot;eeprom.h&amp;quot; definieren.&lt;br /&gt;
Allerdings muss man hier höllisch aufpassen, dass man nicht um eine oder mehrere&lt;br /&gt;
Positionen verrutscht!&lt;br /&gt;
&lt;br /&gt;
Welche der beiden Möglichkeiten man einsetzt, hängt vor allem davon ab, wieviele&lt;br /&gt;
Byte, Word und sonstige Variablen man benutzt. Gewöhnen sollte man sich an beide Varianten können ;)&lt;br /&gt;
&lt;br /&gt;
Kleine Schlussbemerkung:&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc unterstützt die Variante 1 und die Variante 2&lt;br /&gt;
* Der icc-avr Compiler unterstützt nur die Variante 2!&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Bei alten Versionen der avr-libc wurden nicht alle AVR Controller  untersützt. Z.B. bei der avr-libc Version 1.2.3 insbesondere bei AVRs &amp;quot;der neuen Generation&amp;quot; (ATmega48/88/168/169) funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register). In neueren Versionen (z.B. avr-libc 1.4.3 aus WinAVR 20050125) wurde die Zahl der unterstüzten Controller deutlich erweitert und eine Methode zur leichten Anpassung an zukünftige Controller eingeführt.&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Will oder kann man nicht auf die neue Version aktualisieren, kann der dort gezeigte Code auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;, gegebenfalls Schutz vor Unterbrechnung/Interrupt ergänzen &#039;&#039;uint8_t sreg; sreg=SREG; cli(); [EEPROM-Code] ; SREG=sreg; return;&#039;&#039;, siehe Abschnitt Interrupts). Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== EEPROM Register ===&lt;br /&gt;
Um das EEPROM anzusteuern sind drei Register von Bedeutung.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEAR Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL da in einem 8 Bit Register keine 512 Adressen adressiert werden können&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEDR Hier werden die Daten eingetragen die geschrieben werden sollen bzw. es enthält die gelesenen Daten&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EECR Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TABLE BORDER=1&amp;gt;&lt;br /&gt;
&amp;lt;TR&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;Bit&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;1&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;0&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&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;Name&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&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;TD&amp;gt;-&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERIE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEMWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERE&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;Read/Write&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;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&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;Init Value&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;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;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;TD&amp;gt;0&amp;lt;/TD&amp;gt;&lt;br /&gt;
&amp;lt;/TR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/TABLE&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bedeutung der Bits:&amp;lt;/U&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4-7 nicht belegt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 3 (EERIE) - EEPROM Ready Interrupt Enable:&amp;lt;/U&amp;gt; Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7) wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0 wird kein Interrupt ausgelöst.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 2(EEMWE) - EEPROM Master Write Enable:&amp;lt;/U&amp;gt; Dieses Bit bestimmt, daß wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE =0 ist und EEWE = 1 gesetzt wird hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst.&lt;br /&gt;
Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 1 (EEWE) - EEPROM Write Enable:&amp;lt;/U&amp;gt; Dieses Bit löst den Schreibvorgang aus wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist wird dieses Bit automatisch wieder auf 0 gesetzt und sofern EERIE gesetzt ist ein Interrupt ausgelöst. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ein Schreibvorgang sieht typischerweise wie folgt aus:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. EEPROM Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Daten übergeben an EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1 &amp;lt;BR&amp;gt;&lt;br /&gt;
5. (Optinal) Warten bis Schreibvorgang abgeschlossen ist.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 0 EERE – EEPROM Read Enable:&amp;lt;/U&amp;gt; Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit wenn das Bit EEWE=0 ist. ISt der LEsevorgang abgeschlossen wird das Bit wieder auf 0 gesetzt und das EEPROM ist für neue Lese/Schreibbefehle wieder bereit.&amp;lt;BR&amp;gt;&lt;br /&gt;
Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. Bereitschaft zum lesen prüfen (EEWE=0)&amp;lt;BR&amp;gt;&lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Lesezyklus auslösen mit EERE = 1&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Warten bis Lesevorgang abgeschlossen EERE = 0&amp;lt;BR&amp;gt;&lt;br /&gt;
5. Daten abholen aus EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bietet sich printf/sprintf an. &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t s[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( s, &amp;quot;Zählerstand: %d&amp;quot;, value );&lt;br /&gt;
    uart_puts( s );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, die Ausgabe stdout auf eigene Ausgabefunktionen umzuleiten. Dazu wird dem Ausgabemechanismus eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben (wie auch immer die Ausgabe stattfindet). Alle anderen, höheren, Funktionen greifen letztendlich auf diese eine Ausgabefunktion zurück, die die Ausgabe eines einzelnen Zeichens vornimmt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uart_init();&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
    printf( &amp;quot;Hello, world!\n&amp;quot; );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil aller printf-Varianten: Sie sind recht speicherintensiv.&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C- und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gnu-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Inline-Assembler: Die Assembleranweisungen werden direkt in den C-Code integriert. Eine Quellcode-Datei enthält somit C- und Assembleranweisungen&lt;br /&gt;
* Assembler-Dateien: Der Assemblercode befindet sich in eigenen Quellcodedateien. Diese werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung (NOP steht für No Operation). Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=Warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
   /* Verzögern der weiteren Programmausführung um&lt;br /&gt;
      genau 3 Taktzyklen */&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeintlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
&lt;br /&gt;
    /* leere Schleife - wird bei eingeschalteter Compiler-Optimierung   wegoptimiert */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* alternative Methode (keine Optimierung): */&lt;br /&gt;
    volatile uint16_t j;&lt;br /&gt;
    for (j = 0; j &amp;lt; 1000; j++)&lt;br /&gt;
      ;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützlicher &amp;quot;Assembler-Einzeiler&amp;quot; ist der Aufruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;);&#039;&#039;), da hierzu in älteren Versionen der avr-libc keine eigene Funktion existiert (in neueren Versionen &#039;&#039;sleep_cpu()&#039;&#039; aus sleep.h).&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise Delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil, dass die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static inline void delayloop16 (uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
    asm volatile (&amp;quot;cp  %A0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;cpc %B0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;breq 2f               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;1:                    \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;sbiw %0,1             \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;brne 1b               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;2:                    &amp;quot;  &lt;br /&gt;
                  : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
	          : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
    );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Anweisung wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen. Der Zeilenumbruch teilt dem Assembler mit, dass ein neuer Befehl beginnt.&lt;br /&gt;
* Als Sprung-Marken (Labels) werden Ziffern verwendet. Diese speziellen Labels sind mehrfach im Code verwendbar. Gesprungen wird jeweils zurück (b) oder vorwärts (f) zum nächsten ausffindbaren Label.&lt;br /&gt;
&lt;br /&gt;
Das Resultat zeigt ein Blick in die Assembler-Datei, die der Compiler mit der option &amp;lt;tt&amp;gt;-save-temps&amp;lt;/tt&amp;gt; nicht löscht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	cp  r24, __zero_reg__ 	 ;  count&lt;br /&gt;
	cpc r25, __zero_reg__ 	 ;  count&lt;br /&gt;
	breq 2f               &lt;br /&gt;
	1:                    &lt;br /&gt;
	sbiw r24,1             	 ;  count&lt;br /&gt;
	brne 1b               &lt;br /&gt;
	2:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc im Abschnitt [http://www.nongnu.org/avr-libc/user-manual/inline_asm.html Related Pages/Inline Asm]. &lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc Deutsche Einführung in Inline-Assembler]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
Wenn man mit dem AVR Studio arbeitet, kann alternativ auch das standardmäßig erstellte Makefile bearbeitet und folgende Zeilen eingefügt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
## Objects that must be built in order to link&lt;br /&gt;
OBJECTS = (alte Dateien...) useful.o&lt;br /&gt;
&lt;br /&gt;
## Compile&lt;br /&gt;
## Hier folgt eine Liste der gelinkten Dateien, darunter einfügen:&lt;br /&gt;
useful.o: ../useful.S&lt;br /&gt;
	$(CC) $(INCLUDES) $(ASMFLAGS) -c  $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Das war es schon. Allerdings gilt es zu beachten, dass das makefile über &amp;quot;Project -&amp;gt; Configuration options&amp;quot; selbst einzubinden ist, sonst wird es natürlich wieder überschrieben.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel eine Funktion &#039;&#039;superFunc&#039;&#039;, die alle Pins des Ports D auf &amp;quot;Ausgang&amp;quot; schaltet, eine Funktion &#039;&#039;ultraFunc&#039;&#039;, die die Ausgänge entsprechend des übergebenen Parameters schaltet, eine Funktion &#039;&#039;gigaFunc&#039;&#039;, die den Status von Port A zurückgibt und eine Funktion &#039;&#039;addFunc&#039;&#039;, die zwei Bytes zu einem 16-bit-Wort addiert. Die Zuweisungen im C-Code (PORTx = ...) verhindern, dass der Compiler die Aufrufe wegoptimiert und dienen nur zur Veranschaulichung der Parameterübergaben.&lt;br /&gt;
&lt;br /&gt;
Zuerst der Assembler-Code. Der Dateiname sei useful.S:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//; Arbeitsregister (ohne &amp;quot;r&amp;quot;) &lt;br /&gt;
workreg  = 16&lt;br /&gt;
workreg2 = 17&lt;br /&gt;
&lt;br /&gt;
//; Konstante:&lt;br /&gt;
ALLOUT = 0xff&lt;br /&gt;
&lt;br /&gt;
//; ** Setze alle Pins von PortD auf Ausgang **&lt;br /&gt;
//; keine Parameter, keine Rückgabe&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg  // beachte: _SFR_IO_ADDR()&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Setze PORTD auf übergebenen Wert **&lt;br /&gt;
//; Parameter in r24 (LSB immer bei &amp;quot;graden&amp;quot; Nummern)&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zustand von PINA zurückgeben **&lt;br /&gt;
//; Rückgabewerte in r24:r25 (LSB:MSB), hier nur LSB genutzt&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zwei Bytes addieren und 16-bit-Wort zurückgeben **&lt;br /&gt;
//; Parameter in r24 (Summand1) und r22 (Summand2) -&lt;br /&gt;
//;  Parameter sind Word-&amp;quot;aligned&amp;quot; d.h. LSB immer auf &amp;quot;graden&amp;quot;&lt;br /&gt;
//;  Registernummern. Bei 8-Bit und 16-Bit Paramtern somit &lt;br /&gt;
//;  beginnend bei r24 dann r22 dann r20 etc.&lt;br /&gt;
//; Rückgabewert in r24:r25&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   push workreg2&lt;br /&gt;
   clr workreg2&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
   pop workreg2&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
//; oh je - sorry - Mein AVR-Assembler ist eingerostet, hoffe das stimmt so...&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Makefile ist der Name der Assembler-Quellcodedatei einzutragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
ASRC = useful.S&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Aufruf erfolgt dann im C-Code so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
extern void superFunc(void);&lt;br /&gt;
extern void ultraFunc(uint8_t setVal);&lt;br /&gt;
extern uint8_t gigaFunc(void);&lt;br /&gt;
extern uint16_t addFunc(uint8_t w1, uint8_t w2);&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
[...]&lt;br /&gt;
  superFunc();&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
&lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
[...]&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird wieder in der lss-Datei ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
   superFunc();&lt;br /&gt;
 148:	0e 94 f6 00 	call	0x1ec&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
 14c:	85 e5       	ldi	r24, 0x55	; 85&lt;br /&gt;
 14e:	0e 94 fb 00 	call	0x1f6&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
 152:	0e 94 fd 00 	call	0x1fa&lt;br /&gt;
 156:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
  &lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
 158:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 15a:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 15c:	0e 94 ff 00 	call	0x1fe&lt;br /&gt;
 160:	8b bb       	out	0x1b, r24	; 27&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
 162:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 164:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 166:	0e 94 fc 00 	call	0x1f8&lt;br /&gt;
 16a:	89 2f       	mov	r24, r25&lt;br /&gt;
 16c:	99 27       	eor	r25, r25&lt;br /&gt;
 16e:	88 bb       	out	0x18, r24	; 24&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
000001ec &amp;lt;superFunc&amp;gt;:&lt;br /&gt;
// setze alle Pins von PortD auf Ausgang&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1ec:	0f 93       	push	r16&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
 1ee:	0f ef       	ldi	r16, 0xFF	; 255&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg&lt;br /&gt;
 1f0:	01 bb       	out	0x11, r16	; 17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 1f2:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 1f4:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001f6 &amp;lt;ultraFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// setze PORTD auf übergebenen Wert&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
 1f6:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
   ret&lt;br /&gt;
 1f8:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fa &amp;lt;gigaFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Zustand von PINA zurückgeben&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
 1fa:	89 b3       	in	r24, 0x19	; 25&lt;br /&gt;
   ret&lt;br /&gt;
 1fc:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fe &amp;lt;addFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// zwei Bytes addieren und 16-bit-Wort zurückgeben&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1fe:	0f 93       	push	r16&lt;br /&gt;
   push workreg2&lt;br /&gt;
 200:	1f 93       	push	r17&lt;br /&gt;
   clr workreg2&lt;br /&gt;
 202:	11 27       	eor	r17, r17&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
 204:	06 2f       	mov	r16, r22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
 206:	08 0f       	add	r16, r24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
 208:	11 1d       	adc	r17, r1&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
 20a:	c8 01       	movw	r24, r16&lt;br /&gt;
   pop workreg2&lt;br /&gt;
 20c:	1f 91       	pop	r17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 20e:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 210:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zuweisung von Registern zu Parameternummer und die Register für die Rückgabewerte sind in den &amp;quot;Register Usage Guidelines&amp;quot; der avr-libc-Dokumentation erläutert.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/assembler.html avr-libc-Dokumentation: Related Pages/avr-libc and assembler programs]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage avr-libc-Dokumentation: Related Pages/FAQ/&amp;quot;What registers are used by the C compiler?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
== Globale Variablen für Datenaustausch ==&lt;br /&gt;
&lt;br /&gt;
Oftmals kommt man um globale Variablen nicht herum, z.B. um den Datenaustausch zwischen Hauptprogramm und Interrupt-Routinen zu realisieren. &lt;br /&gt;
Hierzu muss man im Assembler wissen, wo genau die Variable vom C-Compiler abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
Hierzu muss die Variable, hier &amp;quot;zaehler&amp;quot; genannt, zuerst im C-Code als Global definiert werden, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
int16_t main (void)&lt;br /&gt;
{&lt;br /&gt;
    // irgendein Code, in dem zaehler benutzt werden kann&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im foldenden Assembler-Beispiel wird der Externe Interrupt0  verwendet, um den Zähler hochzuzählen. Es fehlen die Initialisierungen des Interrupts und die Interrupt-Freigabe, so richtig sinnvoll ist der Code auch nicht, aber er zeigt (hoffentlich) wie es geht.&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit Interrupt-Vektoren gilt beim GCC-Assembler das Gleiche, wie bei C: Man muss die exakte Schreibweise beachten, ansonsten wird nicht der Interrupt-Vektor angelegt, sondern eine neue Funktion - und man wundert sich, dass nichts funktionert (vgl. das AVR-GCC-Handbuch).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
.extern zaehler&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      //; wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     //; Status-Register (SREG) sichern!&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     lds temp,zaehler               //; Wert aus dem Speicher lesen&lt;br /&gt;
     inc temp                       //; bearbeiten&lt;br /&gt;
     sts zaehler,temp               //; und wieder zurückschreiben&lt;br /&gt;
&lt;br /&gt;
     pop temp                       //; die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen im Assemblerfile anlegen ===&lt;br /&gt;
&lt;br /&gt;
Alternativ können Variablen aber auch im Assemblerfile angelegt werden. Dadurch kann auf eine .c-Datei verzichtet werden. Für das obige Beispiel könnte der Quelltext dann die Dateien zaehl_asm.S und zaehl_asm.h abgelegt werden, so dass nur noch zaehl_asm.S mit kompiliert werden müsste.&lt;br /&gt;
&lt;br /&gt;
Anstatt im Assemblerfile über das Schlüsselwort &#039;&#039;.extern &#039;&#039; auf eine vorhandene Variable zu verweisen, wird dazu mit dem Schlüsselwort &#039;&#039;.comm&#039;&#039; die benötigte Anzahl von Bytes für eine Variable reserviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.S&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
//; 1 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 1&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Headerdatei wird dann auf die Variable nur noch verwiesen (Schlüsselwort &#039;&#039;extern&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef ZAEHL_ASM_H&lt;br /&gt;
#define ZAEHL_ASM_H&lt;br /&gt;
&lt;br /&gt;
extern volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu globalen Variablen in C werden so angelegte Variablen nicht automatisch mit dem Wert 0 initialisiert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer als 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Variablen, die größer als &#039;&#039;&#039;ein&#039;&#039;&#039; Byte sind, können in Assembler auf ähnliche Art angesprochen werden. Hierzu müssen nur genug Bytes angefordert werden, um die Variable aufzunehmen. Soll z.B. für den Zähler eine Variable vom Typ &#039;&#039;unsigned long&#039;&#039;, also &#039;&#039;uint32_t&#039;&#039; verwendet werden, so müssen 4 Bytes reserviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die dazugehörige Deklaration im Headerfile wäre dann:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
extern volatile uint32_t zaehler;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen, die größer als ein Byte sind, werden die Werte beginnend mit dem niederwertigsten Byte im RAM abgelegt. Das folgende Codeschnippsel zeigt, wie unter Assembler auf die einzelnen Bytes zugegriffen werden kann. Dazu wird im Interrupt nun ein 32-Bit Zähler erhöht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      // wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     // Status-Register (SREG) sichern !&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     // 32-Bit-Zähler incrementieren&lt;br /&gt;
     lds temp, (zaehler + 0)        // 0. Byte (niederwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 0), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
     lds temp, (zaehler + 1)        // 1. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 1), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 2)        // 2. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 2), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 3)        // 3. Byte (höchstwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 3), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
RAUS:&lt;br /&gt;
     pop temp                       // die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TODO:&#039;&#039;&#039; 16-Bit / 32-Bit Variablen, Zugriff auf Arrays (Strings)&lt;br /&gt;
&lt;br /&gt;
= Anhang =&lt;br /&gt;
&lt;br /&gt;
== Besonderheiten bei der Anpassung bestehenden Quellcodes ==&lt;br /&gt;
&lt;br /&gt;
Einige Funktionen, die in frühren Versionen der avr-libc vorhanden waren, werden inzwischen als veraltet angesehen. Sie sind nicht mehr vorhanden oder als &#039;&#039;deprecated&#039;&#039; (missbilligt) ausgewiesen und Definitionen in &amp;lt;compat/deprecated.h&amp;gt; verschoben. Es empfiehlt sich, vorhandenen Code zu portieren und die alten Funktionen nicht mehr zu nutzen, auch wenn diese noch zur Verfügung stehen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zur Deklaration von Interrupt-Routinen ===&lt;br /&gt;
&lt;br /&gt;
Die Funktionen (eigentlich Makros) &#039;&#039;SIGNAL&#039;&#039; und &#039;&#039;INTERRUPT&#039;&#039; zur Deklaration von Interruptroutinen sollten nicht mehr genutzt werden. &lt;br /&gt;
&lt;br /&gt;
In aktuellen Versionen der avr-libc (z.B. avr-libc 1.4.3 aus WinAVR 20060125) werden Interruptroutinen, die &#039;&#039;&#039;nicht&#039;&#039;&#039; durch andere Interrupts &#039;&#039;&#039;unterbrechbar&#039;&#039;&#039; sind, mit ISR deklariert (siehe Abschnitt im Hauptteil). Auch die Benennung wurden vereinheitlicht und an die üblichen Bezeichnungen in den AVR Datenblättern angepasst. In der Dokumentation der avr-libc sind alte und neue Bezeichnungen in der Tabelle gegenübergestellt. Die erforderlichen Schritte zur Portierung:&lt;br /&gt;
&lt;br /&gt;
* #include von avr/signal.h entfernen&lt;br /&gt;
* SIGNAL duch ISR ersetzen&lt;br /&gt;
* Name des Interrupt-Vektors anpassen (SIG_* durch entsprechendes *_vect)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für die Anpassung zuerst ein &amp;quot;alter&amp;quot; Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer2 Output Compare bei einem ATmega8 */&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE2)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt wird der Vektor mit TIMER2 COMP bezeichnet. Die Bezeichnung in der avr-libc entspricht dem Namen im Datenblatt, Leerzeichen werden durch Unterstriche (_) ersetzt und ein _vect angehängt. &lt;br /&gt;
&lt;br /&gt;
Der neue Code sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt; &lt;br /&gt;
/* signal.h entfällt */&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER2_COMP_vect)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Unklarheiten bezüglich der neuen Vektorlabels hilft (noch) ein Blick in die Headerdatei des entsprechenden Controllers. Für das vorherige Beispiel also der Blick in die Datei iom8.h für den ATmega8, dort findet man die veraltete Bezeichnung unterhalb der aktuellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect		_VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2		_VECTOR(3)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Nachfolgendes mit avr-libc 1.4.5 (in WinAVR 1/2007 behoben - noch eine Weile auskommentiert lassen und dann löschen.&lt;br /&gt;
Konnte in alten Versionen signal.h ohne interrupt.h eingebunden werden, erhält man bei Verwendung der avr-libc Version 1.4.3 (WinAVR 2/2005) beim Compilieren eine Fehlermeldung, da mit signal.h nicht die erforderlichen Definitionen eingebunden werden. Der Lösungsvorschlag in signal.h auch interrupt.h einzubinden, wurde von den avr-libc-Enwicklern akzeptiert und das Problem ist  im Quellcode (CVS) bereits behoben. Es ist aber noch keine avr-libc-&amp;quot;Release&amp;quot; bzw. noch kein WinAVR mit dieser avr-libc-Korrektur verfügbar (Stand 5.2.2006). Will oder kann man den Quellcode nicht aktualisieren, gibt es folgende Alternativen:&lt;br /&gt;
* in Quellcodedateien, in denen nur avr/signal.h eingebunden wird, interrupt.h einbinden (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;). signal.h weiterhin einbinden, falls Kompatibiltät mit alten Versionen gewünscht ist.&lt;br /&gt;
* in der Datei signal.h (bein WinAVR in c:/WinAVR/avr/include/avr/signal.h) ein (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;) ergänzen --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;unterbrechbare&#039;&#039;&#039; Interruptroutinen, die mittels &#039;&#039;INTERRUPT&#039;&#039; deklariert sind, gibt es keinen direkten Ersatz in Form eines Makros. Solche Routinen sind laut Dokumentation der avr-libc in folgender Form zu deklarieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* ** alt ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
INTERRUPT(SIG_OVERFLOW0)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ** neu: ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void TIMER0_OVF_vect(void) __attribute__((interrupt));&lt;br /&gt;
void TIMER0_OVF_vect(void) &lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von &#039;&#039;INTERRUPT&#039;&#039; die Header-Datei &#039;&#039;compat/deprecated.h&#039;&#039; einzubinden. Man sollte bei dieser Gelegenheit jedoch nochmals überprüfen, ob die Funktionalität von &#039;&#039;INTERRUPT&#039;&#039; tatsächlich gewollt ist. In vielen Fällen wurde &#039;&#039;INTERRUPT&#039;&#039; dort genutzt, wo eigentlich &#039;&#039;SIGNAL&#039;&#039; (nunmehr &#039;&#039;ISR&#039;&#039;) hätte genutzt werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;inp&#039;&#039; und &#039;&#039;outp&#039;&#039; zum Einlesen bzw. Schreiben von Registern sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
unsigned char i, j;&lt;br /&gt;
&lt;br /&gt;
// alt:&lt;br /&gt;
  i = inp(PINA);&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  outp(PORTB, j);&lt;br /&gt;
&lt;br /&gt;
// neu (nicht mehr wirklich neu...):&lt;br /&gt;
  i = PINA&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  PORTB = j;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von inp und outp die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Zugriff auf Bits in Registern ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;cbi&#039;&#039; und &#039;&#039;sbi&#039;&#039; zum Löschen und Setzen von Bits sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg. Die Bezeichnung ist ohnehin irreführend da die Funktionen nur für Register mit Adressen im unteren Speicherbereich tatsächlich in die Assembleranweisungen cbi und sbi übersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// alt:&lt;br /&gt;
  sbi(PORTB, PB2);&lt;br /&gt;
  cbi(PORTC, PC1);&lt;br /&gt;
&lt;br /&gt;
// neu (auch nicht mehr wirklich neu...):&lt;br /&gt;
  PORTB |=  (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
  PORTC &amp;amp;= ~(1&amp;lt;&amp;lt;PC1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von sbi und cbi die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden. Wer unbedingt will, kann sich natürlich eigene Makros mit aussagekräftigeren Namen definieren. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &amp;amp;= ~(1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Selbstdefinierte (nicht-standardisierte) ganzzahlige Datentypen ===&lt;br /&gt;
&lt;br /&gt;
Bei den im Folgenden genannten Typdefinitionen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich, bei der Überarbeitung von altem Code die im Abschnitt &#039;&#039;standardisierten ganzzahligen Datentypen&#039;&#039; beschriebenen Datentypen zu nutzen (stdint.h) und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef unsigned char      BYTE;       // besser: uint8_t  aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned short     WORD;       // besser: uint16_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long      DWORD;      // besser: uint32_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long long QWORD;      // besser: uint64_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; BYTE : Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 255. &lt;br /&gt;
&lt;br /&gt;
; WORD : Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 65535. &lt;br /&gt;
&lt;br /&gt;
; DWORD : Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
; QWORD : Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Funktionen im Makefile ==&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken (Libraries/.a-Dateien) hinzufügen ===&lt;br /&gt;
&lt;br /&gt;
Um Funktionen aus Bibliotheken (&amp;quot;echte&amp;quot; Libraries, *.a-Dateien) zu nutzen, sind dem Linker die Namen der Bibliotheken als Parameter zu übergeben. Dazu ist die Option -l (kleines L) vorgesehen, an die der Name der Library angehängt wird. &lt;br /&gt;
&lt;br /&gt;
Dabei ist zu beachten, dass der Name der Library und der Dateiname der Library nicht identisch sind. Der hinter -l angegebene Name entspricht dem Dateinamen der Library ohne die Zeichenfolge &#039;&#039;lib&#039;&#039; am Anfang des Dateinamens und ohne die Endung &#039;&#039;.a&#039;&#039;. Sollen z.B. Funktionen aus einer Library mit dem Dateinamen &#039;&#039;libefsl.a&#039;&#039; eingebunden (gelinkt) werden, lautet der entsprechende Parameter -lefsl (vergl. auch -lm zum Anbinden von libm.a). &lt;br /&gt;
&lt;br /&gt;
In Makefiles wird traditonell eine make-Variable LDLIBS genutzt, in die &amp;quot;l-Parameter&amp;quot; abgelegt werden. Die WinAVR-makefile-Vorlage enthält diese Variable zwar nicht, dies stellt jedoch keine Einschränkung dar, da alle in der make-Variable LDFLAGS abgelegten Parameter an den Linker weitergereicht werden. &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Einbinden von Funktionen aus einer Library efsl (Dateiname libefsl.a)&lt;br /&gt;
LDFLAGS += -lefsl&lt;br /&gt;
# Einbinden von Funktionen aus einer Library xyz (Dateiname libxyz.a)&lt;br /&gt;
LDFLAGS += -lxyz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Liegen die Library-Dateien nicht im Standard Library-Suchpfad, sind die Pfade mittels Parameter &#039;&#039;-L&#039;&#039; ebenfalls anzugeben. (Der vordefinierte Suchpfad kann mittels &#039;&#039;avr-gcc --print-search-dirs&#039;&#039; angezeigt werden.)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Projekt (&amp;quot;superapp2&amp;quot;), in dem der Quellcode von zwei Libraries (efsl und xyz) und der Quellcode der eigentlichen Anwendung in verschiedenen Verzeichnissen mit der folgenden &amp;quot;Baumstruktur&amp;quot; abgelegt sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
superapp2&lt;br /&gt;
|&lt;br /&gt;
+----- efslsource (darin libefsl.a)&lt;br /&gt;
|&lt;br /&gt;
+----- xyzsource (darin libxyz.a)&lt;br /&gt;
|&lt;br /&gt;
+----- firmware (darin Anwendungs-Quellcode und Makefile)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt, dass im Makefile die Verzeichnis efslsource und xyzsource in den Library-Suchpfad aufzunehmen sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
LDFLAGS += -L../efslsource/ -L../xyzsource/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fuse-Bits ===&lt;br /&gt;
&lt;br /&gt;
Zur Berechnung der Fuse-Bits bietet sich neben dem Studium des Datenblattes auch der [http://palmavr.sourceforge.net/cgi-bin/fc.cgi AVR Fuse Calculator] an. Gewarnt werden muss vor der Benutzung von PonyProg, weil dort durch die negierte Darstellung gern Fehler gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Soll die Programmierung von Fuse- und Lockbits automatisiert werden, kann man dies ebenfalls durch Einträge im Makefile vornehmen, die beim Aufruf von &amp;quot;make program&amp;quot; an die genutzte Programmiersoftware übergeben werden. In der makefile-Vorlage von WinAVR (und mfile) gibt es dafuer jedoch keine &amp;quot;Ausfüllhilfe&amp;quot; (Stand 9/2006). Die folgenden Ausführungen gelten für die Programmiersoftware [[AVRDUDE]] (Standard in der WinAVR-Vorlage), können jedoch sinngemäß auf andere Programmiersoftware übertragen werden, die die Angabe der Fuse- und Lockbits-Einstellungen per Kommandozeilenparameter unterstützt (z.B. stk500.exe). Im einfachsten Fall ergänzt man im Makefile einige Variablen, deren Werte natürlich vom verwendeten Controller und den gewünschten Einstellungen abhängen (vgl. Datenblatt Fuse-/Lockbits):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#---------------- Programming Options (avrdude) ----------------&lt;br /&gt;
&lt;br /&gt;
#...&lt;br /&gt;
#Beispiel! f. ATmega16 - nicht einfach uebernehmen! Zahlenwerte anhand&lt;br /&gt;
#--------- des Datenblatts nachvollziehen und gegebenenfalls aendern.&lt;br /&gt;
#&lt;br /&gt;
AVRDUDE_WRITE_LFUSE = -U lfuse:w:0xff:m&lt;br /&gt;
AVRDUDE_WRITE_HFUSE = -U hfuse:w:0xd8:m&lt;br /&gt;
AVRDUDE_WRITE_LOCK  = -U lock:w:0x2f:m&lt;br /&gt;
#...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit diese Variablen auch genutzt werden, ist der Aufruf von avrdude im Makefile entsprechend zu ergänzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Program the device.  &lt;br /&gt;
program: $(TARGET).hex $(TARGET).eep&lt;br /&gt;
# ohne Fuse-/Lock-Einstellungen (nach WinAVR Vorlage Stand 4/2006)&lt;br /&gt;
#	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
#        $(AVRDUDE_WRITE_EEPROM)&lt;br /&gt;
# mit Fuse-/Lock-Einstellungen&lt;br /&gt;
        $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_LFUSE) \&lt;br /&gt;
        $(AVRDUDE_WRITE_HFUSE) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
        $(AVRDUDE_WRITE_EEPROM) $(AVRDUDE_WRITE_LOCK)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weiter Möglichkeit besteht darin, die Fuse- und Lockbit-Einstellungen vom Preprozessor/Compiler generieren zu lassen. Die Fuse-Bits werden dann bei Verwendung von AVRDUDE in eigene Hex-Files geschrieben. Hierzu kann man z.B. folgendes Konstrukt verwenden:&lt;br /&gt;
&lt;br /&gt;
In eine der C-Sourcen wird eine Variable je Fuse-Byte vom Typ &#039;&#039;unsigned char&#039;&#039; deklariert und in eine extra Section gepackt. Dies kann entweder in einem vorhandenen File passieren oder in ein neues (z.B. fuses.c) geschrieben werden. Das File muss im Makefile aber auf jeden Fall mit kompiliert und gelinkt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// tiny 2313 fuses low byte&lt;br /&gt;
#define CKDIV8  7&lt;br /&gt;
#define CKOUT   6&lt;br /&gt;
#define SUT1    5&lt;br /&gt;
#define SUT0    4&lt;br /&gt;
#define CKSEL3  3&lt;br /&gt;
#define CKSEL2  2&lt;br /&gt;
#define CKSEL1  1&lt;br /&gt;
#define CKSEL0  0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses high byte&lt;br /&gt;
#define DWEN       7&lt;br /&gt;
#define EESAVE     6&lt;br /&gt;
#define SPIEN      5&lt;br /&gt;
#define WDTON      4&lt;br /&gt;
#define BODLEVEL2  3&lt;br /&gt;
#define BODLEVEL1  2&lt;br /&gt;
#define BODLEVEL0  1&lt;br /&gt;
#define RSTDISBL   0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses extended byte&lt;br /&gt;
#define SELFPRGEN  0&lt;br /&gt;
&lt;br /&gt;
#define LFUSE         __attribute__ ((section (&amp;quot;lfuses&amp;quot;)))&lt;br /&gt;
#define HFUSE         __attribute__ ((section (&amp;quot;hfuses&amp;quot;)))&lt;br /&gt;
#define EFUSE         __attribute__ ((section (&amp;quot;efuses&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// select ext crystal 3-8Mhz&lt;br /&gt;
unsigned char lfuse LFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;CKDIV8) | (1&amp;lt;&amp;lt;CKOUT) | (1&amp;lt;&amp;lt;CKSEL3) | (1&amp;lt;&amp;lt;CKSEL2) | &lt;br /&gt;
      (0&amp;lt;&amp;lt;CKSEL1) | (1&amp;lt;&amp;lt;CKSEL0) | (0&amp;lt;&amp;lt;SUT1) | (1&amp;lt;&amp;lt;SUT0) );&lt;br /&gt;
unsigned char hfuse HFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;DWEN) | (1&amp;lt;&amp;lt;EESAVE) | (0&amp;lt;&amp;lt;SPIEN) | (1&amp;lt;&amp;lt;WDTON) | &lt;br /&gt;
      (1&amp;lt;&amp;lt;BODLEVEL2) | (1&amp;lt;&amp;lt;BODLEVEL1) | (0&amp;lt;&amp;lt;BODLEVEL0) | (1&amp;lt;&amp;lt;RSTDISBL) );&lt;br /&gt;
unsigned char efuse EFUSE =&lt;br /&gt;
    ((0&amp;lt;&amp;lt;SELFPRGEN));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG: Die Bitpositionen wurden nicht vollständig getestet!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Eine &amp;quot;1&amp;quot; bedeutet hier, dass das Fuse-Bit &#039;&#039;nicht&#039;&#039; programmiert wird - die Funktion also i.A. nicht aktiviert ist. Eine &amp;quot;0&amp;quot; hingegen aktiviert die meisten Funktionen. Dies ist wie im Datenblatt (1 = unprogrammed, 0 = programmed).&lt;br /&gt;
&lt;br /&gt;
Das Makefile muss nun noch um folgende Targets erweitert werden (mit Tabulator einrücken - nicht mit Leerzeichen):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
lfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j lfuses --change-section-address lfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-lfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-lfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U lfuse:w:$(TARGET)-lfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
hfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j hfuses --change-section-address hfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-hfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-hfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U hfuse:w:$(TARGET)-hfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
efuses: build&lt;br /&gt;
        -$(OBJCOPY) -j efuses --change-section-address efuses=0 \&lt;br /&gt;
         -O ihex $(TARGET).elf $(TARGET)-efuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-efuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U efuse:w:$(TARGET)-efuse.hex;&lt;br /&gt;
        fi;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Target &amp;quot;clean&amp;quot; muss noch um die Zeilen&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
        $(REMOVE) $(TARGET)-lfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-hfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-efuse.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
erweitert werden, wenn auch die Fuse-Dateien gelöscht werden sollen.&lt;br /&gt;
&lt;br /&gt;
Um nun die Fusebits des angeschlossenen Controllers zu programmieren muss lediglichein &amp;quot;make lfuses&amp;quot;, &amp;quot;make hfuses&amp;quot; oder &amp;quot;make efuses&amp;quot; gestartet werden.&lt;br /&gt;
Bei den Fuse-Bits ist besondere Vorsicht geboten, da diese das Programmieren des Controllers unmöglich machen können. Also erst programmieren, wenn man einen HV-Programmierer hat oder ein paar Reserve-AVRs zur Hand ;-)&lt;br /&gt;
&lt;br /&gt;
Um weiterhin den &amp;quot;normalen&amp;quot; Flash beschreiben zu können, ist es wichtig, für das Target &amp;quot;*.hex&amp;quot; im Makefile nicht nur &amp;quot;-R .eeprom&amp;quot; als Parameter zu übergeben sondern zusätzlich noch &amp;quot;-R lfuses -R efuses -R hfuses&amp;quot;. Sonst bekommt AVRDUDE Probleme diese Sections in den Flash (wo sie ja nicht hingehören) zu schreiben.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[AVR_Fuses#Vergleich_der_Fuses_bei_verschiedenen_Programmen|Vergleich der Fuses bei verschiedenen Programmen]]&lt;br /&gt;
&lt;br /&gt;
== Externe Referenzspannung des internen Analog-Digital-Wandlers ==&lt;br /&gt;
&lt;br /&gt;
Die minimale (externe) Referenzspannung des ADC darf nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. z.B. beim ATMEGA8 darf sie laut Datenblatt (S.245, Tabelle 103, Zeile &amp;quot;VREF&amp;quot;) 2,0V nicht unterschreiten. HINWEIS: diese Information findet sich erst in der letzten Revision (Rev. 2486O-10/04) des Datenblatts.&lt;br /&gt;
&lt;br /&gt;
Meiner &amp;lt;!-- Wer? - es gibt inzwischen x Leute die mehr oder weniger viel in diesem Artikel geschrieben haben --&amp;gt; eigenen Erfahrung nach kann man aber (auf eigene Gefahr und natürlich nicht für Seriengeräte) durchaus noch ein klein wenig weiter heruntergehen, bei dem von mir unter die Lupe genommenen ATMEGA8L (also die Low-Voltage-Variante) funktioniert der ADC bei 5V Betriebsspannung mit bis zu VREF=1,15V hinunter korrekt, ab 1,1V und darunter digitalisiert er jedoch nur noch Blödsinn). Ich würde sicherheitshalber nicht unter 1,5V gehen und bei niedrigeren Betriebsspannungen mag sich die Untergrenze für VREF am Pin AREF ggf. nach oben&#039;&#039;&#039;(!)&#039;&#039;&#039; verschieben.&lt;br /&gt;
&lt;br /&gt;
In der letzten Revision des Datenblatts ist außerdem korrigiert, dass ADC4 und ADC5 sehr wohl 10 Bit Genauigkeit bieten (und nicht bloß 8 Bit, wie in älteren Revisionen irrtümlich angegeben.)&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* stdio.h, malloc() &lt;br /&gt;
* Code-Optimierungen (&amp;quot;tricks&amp;quot;), siehe auch Application Note [http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf AVR035: Efficient C Coding for AVR]&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
* SPI siehe [http://www.uni-koblenz.de/~physik/informatik/MCU/SPI.pdf SPI Bus mit Atmel AVR]&lt;br /&gt;
* I²C / TWI Bus [http://www.roboternetz.de/wissen/index.php/TWI]&lt;br /&gt;
* Bootloader (bez. auf boot.h)&lt;br /&gt;
* CAN-Bus&lt;br /&gt;
* Einsatz von einfachen Betriebssystemen auf dem AVR&lt;br /&gt;
* Übersicht zu den C bzw. GCC-predefined Makros (__DATE__, __TIME__,...)&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
* ADC ; &lt;br /&gt;
* Timer&lt;br /&gt;
* USB ; Steuerung mit USB&lt;br /&gt;
* Multiplexen Siebensegment&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28247</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=28247"/>
		<updated>2008-05-27T15:59:56Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Vorwort */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[AVR-GCC]] erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Progammiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong]. Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern, weder in Assembler noch in einer anderen Sprache. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR Controllern finden. Bei WinAVR gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden stetig weiterentwickelt. Erläuterungen und Beispiele beziehen sich auf den C-Compiler avr-gcc ab Version 3.4 und die avr-libc ab Version 1.4.3. Die Unterschiede zu älteren Versionen werden im Haupttext und Anhang zwar erläutert, Anfängern sei jedoch empfohlen, die aktuellen Versionen zu nutzen (für MS-Windows: aktuelle Version des [[WinAVR]]-Pakets). &lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in PDF-Form erhältlich bei:&lt;br /&gt;
http://www.siwawi.arubi.uni-kl.de/avr_projects/AVR-GCC-Tutorial_-_www_mikrocontroller_net.pdf&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels avr-gcc/avr-libc zu erstellen und zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR Controllers, der vom avr-gcc Compiler unterstützt wird (alle ATmegas und die meisten AT90, siehe Dokumentation der avr-libc für unterstützte Typen). Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. Brauchbare Testplattform sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. [[AVR#Starterkits|mehr..]]&lt;br /&gt;
* Der avr-gcc Compiler und die avr-libc. Kostenlos erhältlich für nahezu alle Plattformen und Betriebssysteme. Für MS-Windows im Paket [[WinAVR]]; für Unix/Linux siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]]).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
*[[AVR Checkliste]]&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/2 GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  von den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
* Die Beispieleprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweise ist aber auch auf C-Programme übertragbar.&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien um das Problem nachzuvollziehen sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z.B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu zwei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer Integrierten Entwicklungsumgebung (IDE), bei der alle Einstellungen z.B. in Dialogboxen durchgeführt werden können. Unter Anderen kann AVRStudio ab Version 4.12 (kostenlos bei atmel.com) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs für den avr-gcc (ohne Anspruch auf Vollständigkeit): AtmanAvr C (relativ günstig), KamAVR (kostenlos) VMLab (ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand der universellen und plattformunabhängigen Vorgehensweise mittels make und Makefiles näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind lediglich Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xff;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5a)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/;  // (5b)&lt;br /&gt;
   }                         // (5c)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (6)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In der mit (1) markierten Zeile, wird eine sogenannte Header-Datei eingebunden. In io.h sind die Registernamen definiert, die im späteren Verlauf genutzt werden.&lt;br /&gt;
&lt;br /&gt;
* Bei (2) beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion main.&lt;br /&gt;
&lt;br /&gt;
* Die Anschlüsse eines AVR (&amp;quot;Beinchen&amp;quot;) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Richtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B zu Ausgängen.&lt;br /&gt;
&lt;br /&gt;
* (4) stellt die Werte der Ausgänge ein. Die den ersten beiden  Bits des Ports zugeordnete Anschluss (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht-aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential).&lt;br /&gt;
&lt;br /&gt;
* (5) ist die so genannte Hauptschleife (main-loop). Dies ist eine Programmschleife, welche kontinuierlich wiederkehrende Befehle enthält. In diesem Beispiel ist sie leer. Der Controller durchläuft die Schleife immer wieder, ohne das etwas passiert (außer das Strom verbraucht wird). Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Wäre die Schleife nicht vorhanden, würde der Zustand des Controllers nach dem Programmende undefiniert.&lt;br /&gt;
&lt;br /&gt;
* (6) wäre das Programmende. Die Zeile ist nur aus Gründen der C- Kompatibilität enthalten: int main(void) besagt, dass die Funktion einen Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein auf den Controller lauffähiges Programm zu übersetzen,  wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen Makefile (ohne Endung) im selben Verzeichnis in dem auch die Datei main.c mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im folgenden Abschnitt [[AVR-GCC-Tutorial#Exkurs: Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\tmp\gcc_tut\quickstart&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei main.hex, in der der Code für den AVR enthalten ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\tmp\gcc_tut\quickstart&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.B. über In-System-Programming (ISP) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann (z.B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio), kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine LED leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode an einen dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Exkurs: Makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Plattformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.com) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das Makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den im Makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im Makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten Makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt [[AVR-GCC-Tutorial#EEPROM|Speicherzugriffe]]), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendeten Controllers gesetzt. Eine Liste der von avr-gcc und der avr-libc unterstützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien eintragen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
# nicht auskommentiert: EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Das erstellte Makefile und der Code müssen im gleichen Ordner sein, auch sollte der Dateiname nicht verändert werden. &lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem Makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschinencode erzeugt. Beim Update auf eine neue Compilerversion ist zu beachten, dass diese möglicherweise intern andere Optimierungsalgorithmen verwenden und sich dadurch die Größe des Machinencodes etwas ändert, ohne dass man im Quellcode etwas geändert hat.&lt;br /&gt;
&lt;br /&gt;
Als Orientierungswerte die Größe des Maschinencodes bei verschiedenen Optionen für einen nicht näher spezifizierten relativ kleinen Testcode bei Verwendung einer nicht näher spezifizierten Compilerversion. &lt;br /&gt;
&lt;br /&gt;
* -O0 : 12&#039;217 Byte&lt;br /&gt;
* -O1 : 9&#039;128 Byte&lt;br /&gt;
* -O2 : 1&#039;670 Byte&lt;br /&gt;
* -O3 : 3&#039;004 Byte&lt;br /&gt;
* -Os : 1&#039;695 Byte&lt;br /&gt;
&lt;br /&gt;
Im diesem Testfall führt die Option -O2 mit zum kompaktesten Code, dies  allerdings hier nur mit 25 Bytes &amp;quot;Vorsprung&amp;quot;. Es kann durchaus sein, dass nur wenige Programmerweiterungen dazu führen, dass Compilieren mit -Os wieder in kompakteren Code resultiert.&lt;br /&gt;
&lt;br /&gt;
siehe dazu auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/using_tools.html#gcc_optO avr-libc manual Abschnitt Using the gnu-tools/Compiler-Optionen]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_optflags avr-libc Manual FAQ Nr. 16] (Stand avr-libc Version 1.4.5)&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wird hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (großes S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Taktfrequenz ===&lt;br /&gt;
&lt;br /&gt;
Neuere Versionen der WinAVR/Mfile Vorlage für Makefiles beinhalten die Definition einer Variablen F_CPU (WinAVR 2/2005). Darin wird die Taktfrequenz des Controllers in Hertz eingetragen. Die Definition steht dann im gesamten Projekt ebenfalls unter der Bezeichnung F_CPU zur Verfügung (z.B. um daraus  UART-, SPI- oder ADC-Frequenzeinstellungen abzuleiten).&lt;br /&gt;
&lt;br /&gt;
Die Angabe hat rein &amp;quot;informativen&amp;quot; Charakter, die tatsächliche Taktrate wird über den externen Takt (z.B. Quarz) bzw. die Einstellung des internen R/C-Oszillators  bestimmt. Die Nutzung von F_CPU hat also nur Sinn, wenn die Angabe mit dem tatsächlichen Takt übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Innerhalb neuerer Versionen der avr-libc (ab Version 1.2) wird die Definition der Taktfrequenz (F_CPU) zur Berechnung der Wartefunktionen in delay.h genutzt. Diese funktionieren nur dann korrekt, wenn F_CPU mit der tatsächlichen Taktfrequenz übereinstimmt.&lt;br /&gt;
F_CPU muss dazu jedoch nicht unbedingt im makefile definiert werden. Es reicht aus, wird aber bei mehrfacher Anwendung unübersichtlich, vor &#039;&#039;#include &amp;lt;util/delay.h&amp;gt;&#039;&#039; (veraltet: &#039;&#039;#include &amp;lt;avr/delay.h&amp;gt;&#039;&#039;) ein &#039;&#039;#define F_CPU [hier Takt in Hz]UL&#039;&#039; einzufügen. Bei Nutzung von delay.h ist darauf zu achten, dass die Optimierung des Compilers nicht ausgeschaltet ist, sonst wird sehr viel Code erzeugt und die Wartezeit stimmt nicht mit der gewünschten Zeitspanne überein.&lt;br /&gt;
Vgl. dazu  den [http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html entsprechenden Abschnitt der Dokumentation].&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff: (sollte nur noch in Ausnahmefällen genutzt werden)&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
Die Verwendung von Makefiles bietet noch viele weiter Möglichkeiten, einige davon werden im Anhang [[AVR-GCC-Tutorial#Zus.C3.A4tzliche_Funktionen_im_Makefile|Zusätzliche Funktionen im Makfile]] erläutert.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige (Integer) Datentypen =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei stdint.h definiert. &lt;br /&gt;
Zur Nutzung der standardisierten Typen bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// ab avr-libc Version 1.2.0 möglich und empfohlen:&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// veraltet: #include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef signed char        int8_t;&lt;br /&gt;
typedef unsigned char      uint8_t;&lt;br /&gt;
&lt;br /&gt;
typedef short              int16_t;&lt;br /&gt;
typedef unsigned short     uint16_t;&lt;br /&gt;
&lt;br /&gt;
typedef long               int32_t;&lt;br /&gt;
typedef unsigned long      uint32_t;&lt;br /&gt;
&lt;br /&gt;
typedef long long          int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* int8_t steht für einen 8-Bit Integer mit einem Wertebereich -128 bis +127.&lt;br /&gt;
* uint8_t steht für einen 8-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 255&lt;br /&gt;
&lt;br /&gt;
* int16_t steht für einen 16-Bit Integer mit einem Wertebereich -32768 bis +32767.&lt;br /&gt;
* uint16_t steht für einen 16-Bit Integer ohne Vorzeichen (unsigned int) mit einem Wertebereich von 0 bis 65535.&lt;br /&gt;
&lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; werden vorzeichenbehaftete Zahlen abgespeichert. Typen mit vorgestelltem &#039;&#039;u&#039;&#039; dienen der Ablage von postiven Zahlen (inkl. 0).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/(Standard) Integer Types&lt;br /&gt;
&lt;br /&gt;
= Bitfelder =&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&lt;br /&gt;
&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in eine einzelne Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitfelder sparen Platz im RAM, zu Lasten von Platz im Flash, verschlechtern aber unter Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.&lt;br /&gt;
&lt;br /&gt;
Wenn man nur eine paar wenige Variablen vom Typ bool verwenden möchte, kann man&lt;br /&gt;
auch die Headerdatei &amp;lt;stdbool.h&amp;gt; einbinden und sich dann wie gewohnt einen Booltyp anlegen. Variablen dieses Typs brauchen dennoch 1 Byte Speicher, ermöglichen aber eine genaue Unterscheidung zwischen Zahlenvariable und boolscher Variable.&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;C-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum können für allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden&lt;br /&gt;
AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039;, wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output).  Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt.&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend&lt;br /&gt;
angepasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O-Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek (avr-libc) stellt auch Funktionen zur Abfrage eines einzelnen Bits eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist, wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind &#039;&#039;nicht erforderlich&#039;&#039;, man kann auch &amp;quot;einfache&amp;quot; C-Syntax verwenden, die universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) wenn das Bit gesetzt und 0 (&amp;quot;falsch&amp;quot;) wenn es nicht gesetzt ist. Die Negierung des Ausdrucks, also  &#039;&#039;&#039;!&#039;&#039;&#039;(Registername &amp;amp; (1 &amp;lt;&amp;lt; Bitnummer)), entspricht &#039;&#039;bit_is_clear&#039;&#039; und gibt einen Wert &amp;lt;&amp;gt;0 (&amp;quot;wahr&amp;quot;) zurück, falls das Bit nicht gesetzt ist,&lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O-Register einfach wie eine Variable setzen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und outp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben von Bits ====&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausdruck:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit (für 1) eines Bytes hat die Bitnummer 0, das &amp;quot;höchstwertige&amp;quot; (für 128) die Nummer 7.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );    /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3);   /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die warten, bis ein bestimmter Zustand auf einem Bit erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend gewartet&amp;quot; wird. D.h., der Programmablauf bleibt an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den Watchdog ein, muss man darauf achten, dass dieser auch noch getriggert wird (zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Änderung: Habe PINx und PORTx vertauscht.PINx ist für Pull up und PORTx für high bzw low. Michael.L  - &lt;br /&gt;
&lt;br /&gt;
Was sollte diese Aenderung?? Den Rest des Abschnitts auch gelesen? Evtl. die hier nicht erlaeuterten Zusatzfunktion der neuen AVRs durcheinandergeworfen? Aenderung verworfen bis mehr Details.  m.thomas. &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
// Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
&lt;br /&gt;
DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
/* mehr Tipparbeit aber übersichtlicher: */&lt;br /&gt;
DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&lt;br /&gt;
/* Einige Compiler erlauben die Eingabe von Konstanten im &lt;br /&gt;
   Binärformat, z.B. gcc in WinAVR, NICHT aber aus&lt;br /&gt;
   GNU-gcc-Quellen selbst erstellte Compiler. Diese Schreib-&lt;br /&gt;
   weise also nicht nutzen, wenn Code mit anderen &lt;br /&gt;
   ausgetauscht werden soll. */&lt;br /&gt;
DDRB = 0b00011111;    /* direkte Zuweisung &lt;br /&gt;
                         - übersichtlicher &lt;br /&gt;
                         - nicht standardkonform &lt;br /&gt;
                         - selten portabel&lt;br /&gt;
                         - nur bei modifiziertem GCC */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
Weitere Beispiele:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
DDRB = 0xff; &lt;br /&gt;
// Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; DDB0 );&lt;br /&gt;
// Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB &amp;amp;= ~( ( 1 &amp;lt;&amp;lt; DDB3 ) | ( 1&amp;lt;&amp;lt;DDB4) );&lt;br /&gt;
// Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
DDRB |= ( 1 &amp;lt;&amp;lt; DDB0) | ( 1 &amp;lt;&amp;lt; DDB3 );&lt;br /&gt;
// Alle Pins auf Eingang:&lt;br /&gt;
DDRB = 0x00;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&#039;&#039;&lt;br /&gt;
* &#039;&#039;zuerst die Bits im PORTx-Register setzen und&#039;&#039;&lt;br /&gt;
* &#039;&#039;anschließend die Datenrichtung auf Ausgang stellen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Daraus ergibt sich die Abfolge für einen Eingangspin:&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze PORTx: interner Pull-Up aktiv&#039;&#039;&lt;br /&gt;
* &#039;&#039;setze DDRx: Ausgang &amp;quot;high&amp;quot;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bei der Reihenfolge erst DDRx und dann PORTx, kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;&#039;nicht&#039;&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, &#039;&#039;&#039;sondern&#039;&#039;&#039; Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.&amp;lt;/font&amp;gt; (Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände.)&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch.  Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVRs haben sogar an den meisten Pins softwaremäßig zuschaltbare interne Pull-Up Widerstände, welche wir natürlich auch verwenden können.&lt;br /&gt;
&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;DDC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== (Tasten-)Entprellung ====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim Schließen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&lt;br /&gt;
&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen als mehrfache Impulse gezählt wird. Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* bei alter avr-libc: #include &amp;lt;avr/delay.h&amp;gt; */      &lt;br /&gt;
&lt;br /&gt;
/* Einfache Funktion zum Entprellen eines Tasters */&lt;br /&gt;
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)&lt;br /&gt;
{&lt;br /&gt;
    if ( ! (*port &amp;amp; (1 &amp;lt;&amp;lt; pin)) )&lt;br /&gt;
    {&lt;br /&gt;
        /* Pin wurde auf Masse gezogen, 100ms warten   */&lt;br /&gt;
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz&lt;br /&gt;
        _delay_ms(50); &lt;br /&gt;
        if ( *port &amp;amp; (1 &amp;lt;&amp;lt; pin) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Anwender Zeit zum Loslassen des Tasters geben */&lt;br /&gt;
            _delay_ms(50);&lt;br /&gt;
            _delay_ms(50); &lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; PB0 );                 /* PIN PB0 auf Eingang (Taster)            */&lt;br /&gt;
    PORTB |= ( 1 &amp;lt;&amp;lt; PB0 );                 /* Pullup-Widerstand aktivieren            */&lt;br /&gt;
    ...&lt;br /&gt;
    if (debounce(&amp;amp;PINB, PB0))             /* Falls Taster an PIN PB0 gedrueckt..    */&lt;br /&gt;
        PORTD = PIND ^ ( 1 &amp;lt;&amp;lt; PD7 );  /* ..LED an Port PD7 an-&lt;br /&gt;
                                   bzw. ausschalten */&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesem Beispiel ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritische Anwendungen sollte man ein anderes Verfahren nutzen (z.B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).&lt;br /&gt;
&lt;br /&gt;
Zum Thema Entprellen siehe auch:&lt;br /&gt;
* Artikel [[Entprellung]]&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls ben&amp;amp;ouml;tigt, kann eine 16-Bit Variable auch recht einfach manuell in ihre zwei 8-Bit Bestandteile zerlegt werden. Folgendes Beispiel demonstriert dies anhand des pseudo- 16-Bit Registers UBRR.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
typedef union {&lt;br /&gt;
        uint16_t i16;&lt;br /&gt;
        struct {&lt;br /&gt;
                uint8_t i8l;&lt;br /&gt;
                uint8_t i8h;&lt;br /&gt;
        };&lt;br /&gt;
} convert16to8;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
convert16to8 baud;&lt;br /&gt;
baud.i16 = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
UBRRH = baud.i8h;&lt;br /&gt;
UBRRL = baud.i8l;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/*alternativ:*/&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint16_t wFoo16;&lt;br /&gt;
uint8_t bFooLow, bFooHigh;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
wFoo16   = 0xAA55;                 /* zu &amp;quot;zerlegende&amp;quot; 16Bit-Integer */&lt;br /&gt;
bFooHigh = (uint8_t)(wFoo16 &amp;gt;&amp;gt; 8); /* MS-Byte */&lt;br /&gt;
bFooLow  = (uint8_t)(wFoo16);      /* LS-Byte */&lt;br /&gt;
...&lt;br /&gt;
#define us0(Data) ((unsigned char *)(&amp;amp;Data))&lt;br /&gt;
#define us1(Data) ((unsigned char *)((&amp;amp;Data)+1))&lt;br /&gt;
bFooHigh = us1(wFoo16);&lt;br /&gt;
bFoolow  = us0(wFoo16);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen (z.B. ATmega8) teilen sich UBRRH und UCSRC die gleiche Memory-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL) welches Register tats&amp;amp;auml;chlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und &amp;amp;uuml;bergibt den Wert &#039;&#039;3&#039;&#039; und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und &amp;amp;uuml;bergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Bei einigen 16-bit Registern (insbesondere die der 16-bit Timer) erfolgen Schreibzugriffe über das sogenannte &#039;&#039;temporary Register&#039;&#039;. Die Reihenfolge der Zugriffe bestimmt, wann der Wert tatsächlich ins Register geschrieben wird. Typisch wird erst das High-Byte beschrieben (xxxH) und intern im temporary Register zwischengespeichert. Nachdem das Low-Byte (xxxL) geschrieben wurde, setzt der Controller mit diesem und dem im temporary Register zwischengespeicherten Wert für das High-Byte das 16-bit Register. Dabei ist zu beachten, dass intern nur ein temporary Register verfügbar ist, welches in Interruptroutinen mglw. mit einem anderen Wert überschrieben wird, wenn dort ebenfalls 16-bit Register beschrieben werden. avr-gcc/avr-libc berücksichtigen die korrekte Reihenfolge automatisch, wenn die Register mit ihrem &amp;quot;16-bit Label&amp;quot; (ohne H bzw. L) angesprochen werden, dabei ist der Schutz des temporary Registers vor Überschreiben durch Interruptroutinen dennoch zu beachten (im Zweifel beim Schreibzugriff die Interrupts kurzzeitig global deaktivieren).&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t key_pressed(const volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if ( last_state == ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) ) ) {&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return ( *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit) );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    i = key_pressed( &amp;amp;PINB, PB1 );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Im Übrigen können mit volatile gekennzeichnete Variablen auch als const deklariert werden, um sicherzustellen, dass sie nur noch von der Hardware änderbar sind.&lt;br /&gt;
&lt;br /&gt;
= Der UART =&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Über den [[UART]] kann ein AVR leicht mit einer [[RS-232]]-Schnittstelle eines PC oder sonstiger Geräte mit &amp;quot;serieller Schnittstelle&amp;quot; verbunden werden. &lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungen des UART:&lt;br /&gt;
&lt;br /&gt;
* Debug-Schnittstelle: z.B. zur Anzeige von Zwischenergebnissen (&amp;quot;printf-debugging&amp;quot; - hier besser &amp;quot;UART-debugging&amp;quot;) auf einem PC. Auf dem Rechner reicht dazu ein [[RS-232#Terminalprogramme|Terminalprogramm]] (MS-Windows: Hyperterm oder besser Bray-Terminal, [http://www.mikrocontroller.net/forum/read-8-155472.html#new HTerm]; Unix/Linux z.B. minicom). Ein direkter Anschluss ist aufgrund unterschiedlicher Pegel nicht möglich, jedoch sind entsprechende Schnittstellen-ICs wie z.B. ein MAX232 günstig und leicht zu integrieren. Rechner ohne serielle Schnittstelle können über fertige USB-seriell-Adapter angeschlossen werden. &lt;br /&gt;
* &amp;quot;Mensch-Maschine Schnittstelle&amp;quot;: z.B. Konfiguration und Statusabfrage über eine &amp;quot;Kommandozeile&amp;quot; oder Menüs (siehe z.B. Forumsbeitrag [http://www.mikrocontroller.net/topic/52985 Auswertung RS232-Befehle]) &lt;br /&gt;
* Übertragen von gespeicherten Werten: z.B. bei einem Datenlogger&lt;br /&gt;
* Anschluss von Geräten mit serieller Schnittstelle (z.B. (Funk-)Modems, Mobiltelefone, Drucker, Sensoren, &amp;quot;intelligente&amp;quot; LC-Displays, GPS-Empfänger). &lt;br /&gt;
* &amp;quot;Feldbusse&amp;quot; auf RS485/RS422-Basis mittels entsprechenden Bustreiberbausteinen (z.B. MAX485)&lt;br /&gt;
* DMX, Midi etc.&lt;br /&gt;
* LIN-Bus (&#039;&#039;&#039;L&#039;&#039;&#039;ocal &#039;&#039;&#039;I&#039;&#039;&#039;nterconnect &#039;&#039;&#039;N&#039;&#039;&#039;etwork): Preiswerte Sensoren/Aktoren in der Automobiltechnik und darüber hinaus&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben ein bis zwei vollduplexfähigen UART (&#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) schon eingebaut (&amp;quot;Hardware-UART&amp;quot;). &lt;br /&gt;
Übrigens: Vollduplex heißt nichts anderes, als dass der Baustein gleichzeitig senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega, ATtiny) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterscheidet sich vom UART hauptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurationsregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit zeigt an, ob der Sendepuffer bereit ist, um ein zu sendendes Zeichen aufzunehmen. Das Bit wird vom AVR gesetzt (1), wenn der Sendepuffer leer ist. Es wird gelöscht (0), wenn ein Zeichen im Sendedatenregister vorhanden ist und noch nicht in das Sendeschieberegister übernommen wurde. Atmel empfiehlt aus Kompatibilitätsgründen mit kommenden µC, UDRE auf 0 zu setzen, wenn das UCSRA Register beschrieben wird.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
\mathrm{UBRR} = \frac{\mathrm{Taktfrequenz}}{\mathrm{Baudrate} \cdot 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS-232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. &lt;br /&gt;
&amp;lt;!-- &amp;quot;Hackerloesung&amp;quot; auskommentiert - nicht so gut in einem &amp;quot;Einsteiger-Tutorial&amp;quot; - mthomas&lt;br /&gt;
Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfiehlt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== UART initialisieren ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben. Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach gewünschter Funktionsweise die benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Da wir vorerst nur senden möchten und noch keine Interrupts auswerten wollen, gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs mit USART haben mehrere Konfigurationsregister und erfordern eine etwas andere Konfiguration. Für einen ATmega16 z.B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                 // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);     // Asynchron 8N1&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist noch das Baudratenregister &#039;&#039;&#039;UBRR&#039;&#039;&#039; einzustellen. Bei neueren AVRs besteht es aus zwei Registern &#039;&#039;&#039;UBRRL&#039;&#039;&#039; und &#039;&#039;&#039;UBRRH&#039;&#039;&#039;. Der Wert dafür ergibt sich aus der angegebenen Formel durch Einsetzen der Taktfrequenz und der gewünschten Übertragungsrate. Das Berechnen der Formel wird dem [[C-Präprozessor|Präprozessor]] überlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann&lt;br /&gt;
   F_CPU im Makefile definiert werden, eine nochmalige Definition&lt;br /&gt;
   hier wuerde zu einer Compilerwarnung fuehren. Daher &amp;quot;Schutz&amp;quot; durch&lt;br /&gt;
   #ifndef/#endif &lt;br /&gt;
&lt;br /&gt;
   Dieser &amp;quot;Schutz&amp;quot; kann zu Debugsessions führen, wenn AVRStudio &lt;br /&gt;
   verwendet wird und dort eine andere, nicht zur Hardware passende &lt;br /&gt;
   Taktrate eingestellt ist: Dann wird die folgende Definition &lt;br /&gt;
   nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) &lt;br /&gt;
   von AVRStudio - daher Ausgabe einer Warnung falls F_CPU&lt;br /&gt;
   noch nicht definiert: */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000&amp;quot;&lt;br /&gt;
#define F_CPU 4000000UL    // Systemtakt in Hz &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600UL          // Baudrate&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) // Fehler in Promille, 1000 = kein Fehler.&lt;br /&gt;
&lt;br /&gt;
#if ((BAUD_ERROR&amp;lt;990) || (BAUD_ERROR&amp;gt;1010))&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;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    UBRR = UBRR_VAL;&lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&amp;lt;!-- mthomas: warum nicht UL?, wird von AVRStudio auch mit UL übergeben --&amp;gt;&lt;br /&gt;
Wieder für den Mega16 mit zwei Registern für die Baudrateneinstellung eine etwas andere Programmierung. Wichtig ist, dass UBRRH &#039;&#039;&#039;vor&#039;&#039;&#039; UBRRL geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmega16 */&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN);                // UART TX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einige AVR (z.B. ATmega169, ATmega48/88/168, AT90CAN jedoch nicht für z.B. ATmega16/32, ATmega128, ATtiny2313) wird durch die Registerdefinitionen der avr-libc (io*.h) auch für Controller mit zwei UBRR-Registern (UBRRL/UBRRH) ein UBRR bzw. UBRR0 als &amp;quot;16-bit-Register&amp;quot; definiert und man kann auch Werte direkt per UBRR = UBRR_VAL zuweisen. Intern werden dann zwei Zuweisungen für UBRRH und UBRRL generiert. Dies ist nicht bei allen Controllern möglich, da die beiden Register nicht bei allen aufeinanderfolgende Addressen aufweisen. Die getrennte Zuweisung an UBRRH und UBRRL wie im Beispiel gezeigt ist universeller und portabler und daher vorzuziehen.&lt;br /&gt;
&lt;br /&gt;
Die Makros sind sehr praktisch, da sie sowohl automatisch den Wert für UBRR als auch den Fehler in der generierten Baudrate berechnen und im Falle einer Überschreitung (+/-1%) einen Fehler und somit Abbruch im Compilerablauf generieren. Damit können viele Probleme mit &amp;quot;UART sendet komische Zeichen&amp;quot; vermieden werden. Ausserdem kann man mühelos die Einstellung an eine neue Taktfrequenz bzw. Baudrate anpassen, ohne selber rechnen oder in Tabellen nachschlagen zu müssen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. Vorher ist zu prüfen, ob das UART-Modul bereit ist, das zu sendende Zeichen entgegenzunehmen. Die Bezeichnungen des/der Statusregisters mit dem Bit UDRE ist abhängig vom Controllertypen (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    // bei AVR mit einem UART (&amp;quot;classic AVR&amp;quot; z.B. AT90S8515)&lt;br /&gt;
    while (!(USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                  /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&lt;br /&gt;
    /** ODER **/&lt;br /&gt;
&lt;br /&gt;
    // bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                    /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe &amp;quot;String senden&amp;quot; wird durch zwei Funktionen abgearbeitet. Die universelle/controllerunabhängige Funktion uart_puts übergibt jeweils ein Zeichen der Zeichenkette an eine Funktion uart_putc, die abhängig von der vorhandenen Hardware implementiert werden muss. In der Funktion zum Senden eines Zeichens ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// putc fuer AVR mit einem UART (z.B. AT90S8515)&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while(!(USR &amp;amp; (1 &amp;lt;&amp;lt; UDRE)))  /* warte, bis UDR bereit */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = c;                     /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** ODER **/&lt;br /&gt;
&lt;br /&gt;
// bei neueren AVRs andere Bezeichnung fuer die Statusregister, hier ATmega16:&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich */&lt;br /&gt;
    {&lt;br /&gt;
    }                             &lt;br /&gt;
&lt;br /&gt;
    UDR = c;                      /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* puts ist unabhaengig vom Controllertyp */&lt;br /&gt;
void uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != &#039;\0&#039; also ungleich dem &amp;quot;String-Endezeichen&amp;quot; */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die in uart_putc verwendeten Schleifen, in denen gewartet wird bis die UART-Hardware zum senden bereit ist, sind insofern etwas kritisch, da während des Sendens eines Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. empfangenen Zeichen/Bytes zwischengespeichert und in Interruptroutinen an die U(S)ART-Hardware weitergegeben bzw. von ihr ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&lt;br /&gt;
&lt;br /&gt;
=== Schreiben von Variableninhalten ===&lt;br /&gt;
&lt;br /&gt;
Sollen Inhalte von Variablen (Ganzzahlen, Fließkomma) in &amp;quot;menschenlesbarer&amp;quot; Form gesendet werden, ist vor dem Transfer eine Umwandlung in Zeichen (&amp;quot;ASCII&amp;quot;) erforderlich. Bei nur einer Ziffer ist diese Umwandlung relativ einfach: man addiert den ASCII-Wert von Null zur Ziffer und kann diesen Wert direkt senden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_putc (s.o.)&lt;br /&gt;
&lt;br /&gt;
// Ausgabe von 0123456789&lt;br /&gt;
char c;&lt;br /&gt;
&lt;br /&gt;
for (uint8_t i=0; i&amp;lt;=9; ++i) {&lt;br /&gt;
    c = i + &#039;0&#039;;&lt;br /&gt;
    uart_putc( c );&lt;br /&gt;
    // verkuerzt: uart_putc( i + &#039;0&#039; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll mehr als eine Ziffer ausgegeben werden, bedient man sich zweckmäßigerweise vorhandener Funktionen zur Umwandlung von Zahlen in Zeichenketten/Strings. Die Funktion der avr-libc zur Umwandlung von vorzeichenbehafteten 16bit-Ganzzahlen (int16_t) in Zeichenketten heißt &#039;&#039;itoa&#039;&#039; (Integer to ASCII). Man muss der Funktion einen Speicherbereich zur Verarbeitung (buffer) mit Platz für alle Ziffern, das String-Endezeichen (&#039;\0&#039;) und evtl. das Vorzeichen bereitstellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   char s[7];&lt;br /&gt;
   int16_t i = -12345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   itoa( i, s, 10 ); // 10 fuer radix -&amp;gt; Dezimalsystem&lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // da itoa einen Zeiger auf den Beginn von s zurueckgibt verkuerzt auch:&lt;br /&gt;
   uart_puts( itoa( i, s, 10 ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für vorzeichenlose 16bit-Ganzzahlen (uint16_t) exisitert &#039;&#039;utoa&#039;&#039;. Die Funktionen für 32bit-Ganzzahlen (int32_t und uint32_t) heißen &#039;&#039;ltoa&#039;&#039; bzw. &#039;&#039;ultoa&#039;&#039;. Da 32bit-Ganzzahlen mehr Stellen aufweisen können, ist ein entsprechend größerer Pufferspeicher vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Auch Fließkommazahlen (float/double) können mit breits vorhandenen Funktionen in Zeichenfolgen umgewandelt werden, dazu existieren die Funktionen &#039;&#039;dtostre&#039;&#039; und &#039;&#039;dtostrf&#039;&#039;. dtostre nutzt Exponentialschreibweise (&amp;quot;engineering&amp;quot;-Format). (Hinweis: z.Zt. existiert im avr-gcc kein &amp;quot;echtes&amp;quot; double, intern wird immer mit &amp;quot;einfacher Genauigkeit&amp;quot;, entsprechend float, gerechnet.) &lt;br /&gt;
&lt;br /&gt;
dtostrf und dtostre benötigen die libm.a der avr-libc. Bei Nutzung von Makefiles ist der Parameter -lm in in LDFLAGS anzugeben (Standard in den WinAVR/mfile-Makefilevorlagen). Nutzt man AVRStudio als IDE für den GNU-Compiler (gcc-Plugin) ist die libm.a unter Libaries auszuwählen: Project -&amp;gt; Configurations Options -&amp;gt; Libaries -&amp;gt; libm.a mit dem Pfeil nach rechts einbinden. Siehe auch die [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|FAQ]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
/* lt. avr-libc Dokumentation:&lt;br /&gt;
char* dtostrf(&lt;br /&gt;
  double __val,&lt;br /&gt;
  char   __width,&lt;br /&gt;
  char   __prec,&lt;br /&gt;
  char * __s&lt;br /&gt;
)  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   // Pufferspeicher ausreichend groß&lt;br /&gt;
   // evtl. Vorzeichen + width + Endezeichen:&lt;br /&gt;
   char s[8]; &lt;br /&gt;
   float f = -12.345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   dtostrf( f, 6, 3, s ); &lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // verkürzt: uart_puts( dtostrf( f, 7, 3, s ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfangen ==&lt;br /&gt;
=== einzelne Zeichen empfangen ===&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von Zeichen muss der Empfangsteil des UART bei der Initialisierung aktiviert werden, indem das RXEN-Bit im jeweiligen Konfigurationsregister (UCSRB bzw UCSR0B/UCSR1B) gesetzt wird. Im einfachsten Fall wird solange gewartet, bis ein Zeichen empfangen wurde, dieses steht dann im UART-Datenregister (UDR bzw. UDR0 und UDR1 bei AVRs mit 2 UARTS) zur Verfügung (sogen. &amp;quot;Polling-Betrieb&amp;quot;). Ein Beispiel für den ATmega16:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Zusaetzlich zur Baudrateneinstellung und der weiteren Initialisierung: */&lt;br /&gt;
void Usart_EnableRX()&lt;br /&gt;
{&lt;br /&gt;
    UCSRB |= ( 1 &amp;lt;&amp;lt; RXEN );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion blockiert den Programmablauf. Alternativ kann das RXC-Bit in einer Programmschleife abgefragt werden und dann nur bei gesetztem RXC-Bit UDR ausgelesen werden. Eleganter und in den meisten Anwendungsfällen &amp;quot;stabiler&amp;quot; ist die Vorgehensweise, die empfangenen Zeichen in einer Interrupt-Routine einzulesen und zur späteren Verarbeitung in einem Eingangsbuffer (FIFO-Buffer) zwischenzuspeichern. Dazu existieren fertige und gut getestete [[Libraries|Bibliotheken]] &amp;lt;!-- &amp;quot;echte Libraries&amp;quot; (.a) wie im Verweis beschrieben sind hier eigentlich nicht gemeint, verwirrt hier etwas, da AVR-&amp;quot;Libraries&amp;quot; meist per #defines anpassbare Source-Codes sind, vielleicht so: --&amp;gt; und Quellcodekomponenten (z.B. UART-Library von P. Fleury, procyon-avrlib und einige in der &amp;quot;Academy&amp;quot; von avrfreaks.net).&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html Dokumenation der avr-libc/stdlib.h]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_printf Die Nutzung von printf]&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&amp;lt;!-- nimmermehr: * siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (printf etc.) im [[AVR-Tutorial:_UART]]. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: 9bit&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Zeichenketten (Strings) ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Zeichenketten, muß man sich zunächst darüber im klaren sein, daß es ein Kriterium geben muß, an dem der µC erkennen kann, wann ein String zu Ende ist. Sehr oft wird dazu das Zeichen &#039;Return&#039; benutzt, um das Ende eines Strings zu markieren. Dies ist vom Benutzer einfach eingebbar und er ist auch daran gewöhnt, daß er eine Eingabezeile mit einem Druck auf die Return Taste abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Prinzipiell gibt es jedoch keine Einschränkung bezüglich dieses speziellen Zeichens. Es muß nur sichergestellt werden, daß dieses spezielle &#039;Ende eines Strings&#039; - Zeichen nicht mit einem im Text vorkommenden Zeichen verwechselt werden kann. Wenn also im zu übertragenden Text beispielsweise kein &#039;;&#039; vorkommt, dann spricht nichts dagegen, einen String mit einem &#039;;&#039; abschließen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird die durchaus übliche Annahme getroffen, daß eine Stringübertragung identisch ist mit der Übertragung einer Textzeile und daher mit einem Return (&#039;\n&#039;) abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Das Problem der Übertragung eines Strings reduziert sich damit auf die Aufgabenstellung: Empfange und Sammle Zeichen in einem char Array, bis entweder das Array voll ist oder das &#039;String Ende Zeichen&#039; empfangen wurde. Danach wird der empfangene Text noch mit einem &#039;\0&#039; Zeichen abgeschlossen um einen Standard C-String daraus zu machen, mit dem dann weiter gearbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_gets( char* Buffer, uint8_t MaxLen )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t NextChar;&lt;br /&gt;
  uint8_t StringLen = 0;&lt;br /&gt;
&lt;br /&gt;
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen&lt;br /&gt;
&lt;br /&gt;
                                  // Sammle solange Zeichen, bis:&lt;br /&gt;
                                  // * entweder das String Ende Zeichen kam&lt;br /&gt;
                                  // * oder das aufnehmende Array voll ist&lt;br /&gt;
  while( NextChar != &#039;\n&#039; &amp;amp;&amp;amp; StringLen &amp;lt; MaxLen - 1 ) {&lt;br /&gt;
    *Buffer++ = NextChar;&lt;br /&gt;
    StringLen++;&lt;br /&gt;
    NextChar = uart_getc();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
                                  // Noch ein &#039;\0&#039; anhängen um einen Standard&lt;br /&gt;
                                  // C-String daraus zu machen&lt;br /&gt;
  *Buffer = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf ist darauf zu achten, dass das empfangende Array auch mit einer&lt;br /&gt;
vernünftigen Größe definiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
  char Line[40];      // String mit maximal 39 zeichen&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) );&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Interruptbetrieb==&lt;br /&gt;
&lt;br /&gt;
Beim ATMEGA8 ist es möglich, dass RXCIE Bit im Register UCSRB zu setzen.&lt;br /&gt;
Dieser Interrupt wird immer ausglöst, wenn Daten erfolgreich empfangen wurden.&lt;br /&gt;
Zusätzlich braucht man die Routine:&lt;br /&gt;
&lt;br /&gt;
ISR (USART_RXC_vect) {&lt;br /&gt;
   //irgendein Code&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
natürlich muss &amp;quot;Global Interrupt Enable&amp;quot; Aktiviert sein.&lt;br /&gt;
!! Nur getestet beim ATMEGA8 !!&lt;br /&gt;
&lt;br /&gt;
[BAUSTELLE! Aus Lerngründen eventuell als eigenen UART-Interrupt-Block hinter den grundlegenden Interrupt-Teil im Tutorial und hier eine kurze Einführung und einen Verweis darauf anbieten.]&lt;br /&gt;
&lt;br /&gt;
* Unterschied Polling-Betrieb (bisher, oben) und Interrupt-Betrieb&lt;br /&gt;
* Empfangen (Receive)&lt;br /&gt;
** Verändertes UART-Init, ISR (RXC), ggf. Fallstricke ([http://www.mikrocontroller.net/topic/84256#707214 UDR in der ISR lesen!]), Philosophie einer ISR (kurz und schmerzlos), Datenaustausch ISR zu Restprogramm (volatile)&lt;br /&gt;
** Einfachstbeispiel ([http://www.mikrocontroller.net/topic/84228#707052 Echo] (noch buggy beim Datenzugriff, siehe Lit. 2+3!)), ggf. LED zur ISR-Empfangsanzeige oder Overflow-Anzeige&lt;br /&gt;
* FIFO-Puffer, Ringpuffer&lt;br /&gt;
* Senden (Transmit)&lt;br /&gt;
** Variante &amp;quot;UART Data Register Empty&amp;quot; (UDRE) &lt;br /&gt;
** Variante &amp;quot;UART Transmit Complete&amp;quot; (TXC) &lt;br /&gt;
* Fertige UART-Bibliotheken (-&amp;gt;Fleury, -&amp;gt;Procyon)&lt;br /&gt;
* Literatur: &lt;br /&gt;
** 1/ [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=48188 avrfreaks.net Tutorial] inkl. Diskussion (engl.)&lt;br /&gt;
** 2/ avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_16bitio Why do some 16-bit timer registers sometimes get trashed?]&lt;br /&gt;
** 3/ [[Interrupt]] und atomarer Datenzugriff&lt;br /&gt;
&lt;br /&gt;
==Software-UART==&lt;br /&gt;
&lt;br /&gt;
Falls die Zahl der vorhandenen Hardware-UARTs nicht ausreicht, können weitere Schnittstellen über sogennante Software-UARTs ergänzt werden. Es gibt dazu (mindestens) zwei Ansätze: &lt;br /&gt;
* Der bei AVRs üblichste Ansatz basiert auf dem Prinzip, dass ein externer Interrupt-Pin für den Empfang (&amp;quot;RX&amp;quot;) genutzt wird. Das Startbit löst den Interrupt aus, in der Interrupt-Routine (ISR) wird der externe Interrupt deaktiviert und ein Timer aktiviert. In der Interrupt-Routine des Timers wird der Zustand des Empfangs-Pins entsprechend der Baudrate abgetastet. Nach Empfang des Stop-Bits wird der externe Interrupt wieder aktiviert. Senden kann über einen beliebigen Pin (&amp;quot;TX&amp;quot;) erfolgen, der entsprechend der Baudrate und dem zu sendenden Zeichen auf 0 oder 1 gesetzt wird. Die Implementierung ist nicht ganz einfach, es existieren dazu aber fertige Bibliotheken (z.B. bei [http://www.avrfreaks.net/ avrfreaks] oder in der [http://hubbard.engr.scu.edu/embedded/avr/avrlib/ Procyon avrlib]).&lt;br /&gt;
* Ein weiterer Ansatz erfordert keinen Pin mit &amp;quot;Interrupt-Funktion&amp;quot; aber benötigt mehr Rechenzeit. Jeder Input-Pin kann als Empfangspin (RX) dienen. Über einen Timer wird der Zustand des RX-Pins mit einem vielfachen der Baudrate abgetastet (dreifach scheint üblich) und High- bzw. Lowbits anhand einer Mindestanzahl identifiziert. (Beispiel: &amp;quot;Generic Software Uart&amp;quot; Application-Note von IAR)&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (z.B. ATtiny26 oder ATmega48,88,168,169) verfügen über ein Universal Serial Interface (USI), das teilweise UART-Funktion übernehmen kann. Atmel stellt eine Application-Note bereit, in der die Nutzung des USI als UART erläutert wird (im Prinzip &amp;quot;Hardware-unterstützter Software-UART&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==Handshaking==&lt;br /&gt;
Wenn der Sender ständig sendet, wird irgendwann der Fall eintreten, daß der Empfänger nicht bereit ist, neue Zeichen zu empfangen. In diesem Fall muß durch ein &#039;&#039;&#039;Handshake-Verfahren&#039;&#039;&#039; die Situation bereinigt werden. Handshake bedeutet nichts anderes, als daß der Empfänger dem Sender mitteilt, daß er zur Zeit keine Daten annehmen kann und der Sender die Übertragung der nächsten Zeichen solange einstellen soll, bis der Empfänger signalisiert, daß er wieder Zeichen aufnehmen kann.&lt;br /&gt;
===Hardwarehandshake (RTS/CTS)===&lt;br /&gt;
Beim Hardwarehandshake werden zusätzlich zu den beiden Daten-Übertragungsleitungen noch 2 weitere Leitungen benötigt: &#039;&#039;&#039;RTS&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;equest &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end) und &#039;&#039;&#039;CTS&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end). Jeder der beiden Kommunikationspartner ist verpflichtet, bevor ein Zeichen gesendet wird, den Zustand der &#039;&#039;&#039;RTS&#039;&#039;&#039; Leitung zu überprüfen. Nur wenn die Gegenstelle darauf Empfangsbereitschaft signalisiert, darf das Zeichen gesendet werden. Um der Gegenstelle zu signalisieren, daß sie zur Zeit keine Zeichen schicken soll, wird die Leitung &#039;&#039;&#039;CTS&#039;&#039;&#039; benutzt.&lt;br /&gt;
&lt;br /&gt;
===Softwarehandshake (XON/XOFF)===&lt;br /&gt;
Beim Softwarehandshake sind keine speziellen Leitungen notwendig. Statt dessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, daß Senden einzustellen bzw. wieder aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;XOFF&#039;&#039;&#039; Aufforderung das Senden einzustellen&lt;br /&gt;
* &#039;&#039;&#039;XON&#039;&#039;&#039;  Gegenstelle darf wieder senden&lt;br /&gt;
&lt;br /&gt;
Nachteilig bei einem Softwarehandshake ist es, daß dadurch keine direkte binäre Datenübertragung mehr möglich ist. Von den möglichen 256 Bytewerten werden ja 2 (nämlich &#039;&#039;&#039;XON&#039;&#039;&#039; und &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus.&lt;br /&gt;
&lt;br /&gt;
==Fehlersuche==&lt;br /&gt;
Erstaunlich oft wird im Forum der Hilferuf laut: &amp;quot;Meine UART funktioniert nicht, was mache ich falsch&amp;quot;. In der überwiegenden Mehrzahl der Fälle stellt sich dann heraus, daß es sich um ein Hardwareproblem handelt, wobei da wiederrum der Löwenanteil auf das Konto einer nicht korrekt eingestellten Taktrate geht: Der µC benutzt nicht einen angeschlossenen Quarz, so wie er auch im Programm eingetragen ist, sondern läuft immer noch mit dem internen RC-Takt. Daraus resultiert aber auch, daß der Baudraten Konfigurationswert falsch berechnet wird.&lt;br /&gt;
&lt;br /&gt;
Eine Checkliste zum Aufspüren solcher Fehler findet sich [[AVR_Checkliste#UART.2FUSART|hier]].&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Analoge Eingangswerte werden in der Regel über den AVR Analog-Digital-Converter (AD-Wandler, ADC) eingelesen, der in vielen Typen verfügbar ist (typisch 10bit Auflösung). Durch diesen werden analogen Signale (Spannungen) in digitale Zahlenwerte gewandelt. Bei AVRs, die über keinen internen AD-Wandler verfügen (z.B. ATmega162, ATtiny2313), kann durch externe Beschaltung (R/C-Netzwerk und &amp;quot;Zeitmessung&amp;quot;) die Funktion des AD-Wandlers &amp;quot;emuliert&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
Es existieren keine AVRs mit eingebautem Digital-Analog-Konverter (DAC). Diese Funktion muss durch externe Komponenten nachgebildet werden (z.B. PWM und &amp;quot;Glättung&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Unabhängig davon besteht natürlich immer die Möglichkeit, spezielle Bausteine zur Analog-Digital- bzw. Digital-Analog-Wandlung zu nutzen und diese über eine digitale Schnittstelle (z.b. SPI oder I2C) mit einem AVR anzusteuern.&lt;br /&gt;
&lt;br /&gt;
== AC (Analog Comparator) ==&lt;br /&gt;
&lt;br /&gt;
Der Comparator vergleicht 2 Spannungen an den Pins AIN0 und AIN1 und gibt einen Status aus welche der beiden Spannungen größer ist. AIN0 Dient dabei als Referenzspannung (Sollwert) und AIN1 als Vergleichsspannung (Istwert). Als Referenzspannung kann auch alternativ eine interne Referenzspannung ausgewählt werden.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Liegt die Vergleichsspannung (IST) unter der der Referenzspannung (SOLL) gibt der Comperator eine logische 0 aus. ISt die Vergleichsspannung hingegen größer als die Referenzspannung wird eine logische 1 ausgegeben.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
Der Comperator arbeitet völlig autark bzw. parallel zum Prozessor. Für mobile Anwendungen empfiehlt es sich ihn abzuschalten sofern er nicht benötigt wird, da er ansonsten Strom benötigt. Der Comparator kann Interruptgesteuert abgefragt werden oder im Pollingbetrieb.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Steuer- bzw. Statusregister ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ACSR (0x28) - Analog Comparator Status Register&amp;lt;BR&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACD&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;AINBG&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACO&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACI&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ACIS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | n/a&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 7 ACD - Analog Comperator Disable: 0 = Comparator ein, 1 = Comparator aus. Wird dieses Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 ACIE ggf. abgeschaltet werden.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 6 AINBG - Analog Comparator Bandgap Select: Ermöglicht das umschalten zwischen interner und externer Referenzspannung. 1 = interne (~1,3 Volt), 0 = externe Referenzspannung (an Pin AIN0)&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 5 ACO - Analog Coparator Output: Hier wird das Ergebnis des Vergleichs angezeigt. Es liegt typischerweise nach 1-2 Taktzyklen vor. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ergebnis: &amp;lt;BR&amp;gt;IST &amp;lt; SOLL --&amp;gt; 0&amp;lt;BR&amp;gt;&lt;br /&gt;
IST &amp;gt; SOLL --&amp;gt; 1&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4 ACI - Analog Comparator Interrupt Flag: Dieses Bit wird von der Hardware gesetzt wenn ein Interruptereignis das in Bit 0 und 1 definiert ist eintritt. Dieses Bit löst noch keinen Interrupt aus! Die Interruptroutine wird nur dann ausgeführt wenn das Bit 3 ACIE gesetzt ist und global Interrupts erlaubt sind (I-Bit in SREG=1). Das Bit 4 ACI wird wieder gelöscht wenn die Interruptroutine ausgeführt wurde oder wenn manuell das Bit auf 1 gesetzt wird. Das Bit kann für Abfragen genutzt werden, steuert oder konfuguriert aber nicht den Comparator.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 3 ACIE - Analog Comparator Interrupt Enable: Ist das Bit auf 1 gesetzt wird immer ein Interrupt ausgelöst wenn das Ereignis das in Bit 1 und 0 definiert ist eintritt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 2 ACIC - Analog Comparator Input Capture Enable: Wird das Bit gesetzt wird der Comparatorausgang intern mit dem Counter 1 verbunden. Es könnten damit z.b. die Anzahl der Vergleiche im Counter1 gezählt werden. Um den Comparator an den Timer1 Input Capture Interrupt zu verbinden muß im Timerregister das TICIE1 Bit auf 1 gesetzt werden. Der Trigger wird immer dann ausgelöst wenn das in Bit 1 und 0 definierte Ereignis eintritt.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 1,0 ACIS1,ACIS0 - Analog Comparator Interrupt select: Hier wird definiert welche Ereignisse einen Interrupt auslösen sollen:&amp;lt;BR&amp;gt;&lt;br /&gt;
00 = Interrupt auslösen bei jedem Flankenwechsel&amp;lt;BR&amp;gt;&lt;br /&gt;
10 = Interrupt auslösen bei fallender Flanke&amp;lt;BR&amp;gt;&lt;br /&gt;
11 = Interrupt auslösen bei steigender Flanke&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Werden diese Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muß das Bit 3 gelöscht werden.&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der Analog-Digital-Konverter (ADC) wandelt analoge Signale in digitale Werte um, welche vom Controller interpretiert werden können. Einige AVR-Typen haben bereits einen mehrkanaligen Analog-Digital-Konverter eingebaut. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst werden kann, wird durch die Auflösung des ADC in Anzahl Bits angegeben, man hört bzw. liest jeweils von 8-Bit-ADC oder 10-Bit-ADC oder noch höher. ADCs die in AVRs enthalten sind haben zur Zeit eine maximale Auflösung von 10-Bit.&lt;br /&gt;
&lt;br /&gt;
Ein ADC mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des Analog-Digital-Konverters ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Der interne ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem Analog-Digital-Wandler (ADC) zurückgreifen, die über mehrere Kanäle verfügen. Kanäle heißt in diesem Zusammenhang, dass zwar bis zu zehn analoge Eingänge am AVR verfügbar sind, aber nur ein &amp;quot;echter&amp;quot; Analog-Digital-Wandler zur Verfügung steht, vor der eigentlichen Messung ist also einzustellen, welcher Kanal (&amp;quot;Pin&amp;quot;) mit dem Wandler verbunden und gemessen wird.&lt;br /&gt;
&lt;br /&gt;
Die Umwandlung innerhalb des AVR basiert auf der schrittweisen Näherung. Beim AVR müssen die Pins &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AVCC&#039;&#039;&#039; beschaltet werden. Für genaue Messungen sollte AVCC über ein L-C Netzwerk mit VCC verbunden werden, um Spannungsspitzen und -einbrüche vom Analog-Digital-Wandler fernzuhalten. Im Datenblatt findet sich dazu eine Schaltung, die 10uH und 100nF vorsieht.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Analog-Digital-Wandlung wird auf eine Referenzspannung bezogen. Aktuelle AVRs bieten drei Möglichkeiten zur Wahl dieser Spannung:&lt;br /&gt;
&lt;br /&gt;
* Eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; am Anschlusspin &#039;&#039;&#039;AREF&#039;&#039;&#039;. Die minimale (externe) Referenzspannung darf jedoch nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. &lt;br /&gt;
&lt;br /&gt;
* Verfügt der AVR über eine interne Referenzspannung, kann diese genutzt werden. Alle aktuellen AVRs mit internem AD-Wandler sollten damit ausgestattet sein (vgl. Datenblatt: 2,56V oder 1,1V je nach Typ). Das Datenblatt gibt auch über die Genauigkeit dieser Spannung Auskunft.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;interne&#039;&#039;&#039; Referenzspannung wird auf &#039;&#039;&#039;Vcc&#039;&#039;&#039; bezogen, eine externe Referenzspannung auf &#039;&#039;&#039;GND (Masse)&#039;&#039;&#039;. Davon unabhängig werden digitalisierte Spannungen &#039;&#039;&#039;immer&#039;&#039;&#039; auf GND bezogen. Beim ATMEGA8 z.B. ist der Pin AREF über 32kOhm mit GND verbunden, d.h. man muss diese doch extrem niedrige Eingangsimpedanz mit in die Berechnung für einen Spannungsteiler einbeziehen, bzw. kann diesen Widerstand als R2 gleich mit benutzen. Formel für Spannungsteiler: Udiv = U / ((R1 + R2) / R2)&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung von Vcc oder der internen Referenz wird empfohlen, einen Kondensator zwischen dem AREF-Pin und GND anzuordnen. Die Festlegung, welche Spannungsreferenz genutzt wird, erfolgt z.B. beim ATmega16 mit den Bits REFS1/REFS0 im ADMUX-Register. Die zu messende Spannung muss im Bereich zwischen &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AREF&#039;&#039;&#039; (egal ob intern oder extern) liegen. &lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; kann in zwei verschiedenen Betriebsarten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
; Einfache Wandlung (Single Conversion) : In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestoßen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
; Frei laufend (Free Running) : In dieser Betriebsart erfasst der Wandler permanent die anliegende Spannung und schreibt diese in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Die Register des ADC ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; verfügt über eigene Register. Im Folgenden die Registerbeschreibung eines  ATMega16, welcher über 8 ADC-Kanäle verfügt. Die Register unterscheiden sich jedoch nicht erheblich von denen anderer AVRs (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister A.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den &#039;&#039;&#039;ADC&#039;&#039;&#039; verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADEN&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able)&lt;br /&gt;
:Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren. Wenn das Bit nicht gesetzt ist, können die Pins wie normale I/O-Pins verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADSC&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion)&lt;br /&gt;
:Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.&lt;br /&gt;
:Wenn das Bit nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.&lt;br /&gt;
:Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADFR&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;F&#039;&#039;&#039;ree &#039;&#039;&#039;R&#039;&#039;&#039;un select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Ist das Bit auf 1 gesetzt arbeitet der ADC im Freerunning Modus. Dabe iwird das Datenregister permanent aktualisiert. Ist das Bit hingegen auf 0 gesetzt macht der ADC nur eine Single Conversion. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIF&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag)&lt;br /&gt;
:Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt, sobald eine Umwandlung erfolgt ist und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert wurde. Das Bit wird bei lesendem Zugriff auf &#039;&#039;&#039;ADC(L,H)&#039;&#039;&#039; automatisch (d.h. durch die Hardware) gelöscht.&lt;br /&gt;
:Wenn das &#039;&#039;&#039;ADIE&#039;&#039;&#039; Bit sowie das &#039;&#039;&#039;I-Bit&#039;&#039;&#039; im AVR &#039;&#039;&#039;Statusregister&#039;&#039;&#039; gesetzt ist, wird der &#039;&#039;&#039;ADC Interrupt&#039;&#039;&#039; ausgelöst und die Interrupt-Behandlungsroutine aufgerufen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn die Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 in das Register geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIE&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;, dann wird der &#039;&#039;&#039; ADC-Interrupt&#039;&#039;&#039; aktiviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADPS2...ADPS0&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;P&#039;&#039;&#039;rescaler &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
:Der &#039;&#039;&#039;ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz ergibt.&lt;br /&gt;
:Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
TF_{min}=\frac{CLK}{200\,\mathrm{kHz}}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50\,\mathrm{kHz}}=\frac{4000000}{50000}=\mathbf{80}&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im Interesse der schnelleren Wandlungszeit werden wir hier den Faktor 32 einstellen.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCL&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ADCH&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC &#039;&#039;&#039; Data Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn eine Umwandlung abgeschlossen ist, befindet sich der gemessene Wert in&lt;br /&gt;
diesen beiden Registern. Von &#039;&#039;&#039;ADCH&#039;&#039;&#039; werden nur die beiden niederwertigsten Bits verwendet. Es müssen immer beide Register ausgelesen werden, und zwar immer &#039;&#039;&#039;in der Reihenfolge: ADCL, ADCH&#039;&#039;&#039;. &lt;br /&gt;
Der effektive Messwert ergibt sich dann zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCL;       // mit uint16_t x&lt;br /&gt;
x += (ADCH&amp;lt;&amp;lt;8); // in zwei Zeilen (LSB/MSB-Reihenfolge und&lt;br /&gt;
                // C-Operatorpriorität sichergestellt)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADMUX&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Mu&#039;&#039;&#039;ltiple&#039;&#039;&#039;x&#039;&#039;&#039;er Select Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Register wird der zu messende Kanal ausgewählt. Beim 90S8535&lt;br /&gt;
kann jeder Pin von Port A als &#039;&#039;&#039;ADC&#039;&#039;&#039;-Eingang verwendet werden (=8 Kanäle).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADLAR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX4&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX3&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;REFS1...REFS0&#039;&#039;&#039; (&#039;&#039;&#039;Ref&#039;&#039;&#039;erence&#039;&#039;&#039;S&#039;&#039;&#039;election Bits)&lt;br /&gt;
:Mit diesen Bits kann die Referenzspannung eingestellt werden:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;REFS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Referenzspanung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Externes AREF&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | AVCC als Referenz&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Interne 2,56 Volt&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADLAR&#039;&#039;&#039; (&#039;&#039;&#039;ADC &#039;&#039;&#039; &#039;&#039;&#039;L&#039;&#039;&#039;eft &#039;&#039;&#039;A&#039;&#039;&#039;djust &#039;&#039;&#039;R&#039;&#039;&#039;esult)&lt;br /&gt;
:Das ADLAR Bit verändert das Aussehen des Ergebnisses der AD-Wandlung. Bei einer logischen 1 wird das Ergebnis linksbündig ausgegeben, bei einer 0 rechtsündig. Eine Änderung in diesem Bit beeinflusst das Ergebnis sofort, ganz egal ob bereits eine Wandlung läuft.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX4...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 5 Bits wird der zu messende Kanal bestimmt. Wenn man einen einfachen 1-kanaligen ADC verwendet wird einfach die entsprechende Pinnummer des Ports in die Bits 0...2 eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
:Eine Empfehlung ist deswegen diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren des ADC ====&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039; ADC&#039;&#039;&#039; zu aktivieren, müssen wir das &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bit im &#039;&#039;&#039;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
Ein kleines Beispiel für den &amp;quot;single conversion&amp;quot;-Mode bei einem ATmega169 und Nutzung der internen Referenzspannung (beim &#039;169 1,1V bei anderen AVRs auch 2,56V). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler einstellen. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1023 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint16_t ReadChannel(uint8_t mux)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
&lt;br /&gt;
  ADMUX = mux;                      // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler &lt;br /&gt;
                               // setzen auf 8 (1) und ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  /* nach Aktivieren des ADC wird ein &amp;quot;Dummy-Readout&amp;quot; empfohlen, man liest&lt;br /&gt;
     also einen Wert und verwirft diesen, um den ADC &amp;quot;warmlaufen zu lassen&amp;quot; */&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
     ;     // auf Abschluss der Konvertierung warten &lt;br /&gt;
  }&lt;br /&gt;
  result = ADCW;  // ADCW muss einmal gelesen werden,&lt;br /&gt;
                  // sonst wird Ergebnis der nächsten Wandlung&lt;br /&gt;
                  // nicht übernommen.&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  result = 0; &lt;br /&gt;
  for( i=0; i&amp;lt;4; i++ )&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);            // eine Wandlung &amp;quot;single conversion&amp;quot;&lt;br /&gt;
    while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {&lt;br /&gt;
      ;   // auf Abschluss der Konvertierung warten&lt;br /&gt;
    }&lt;br /&gt;
    result += ADCW;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Beispielaufrufe: */&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
&lt;br /&gt;
  adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -&amp;gt; Channel 0 */&lt;br /&gt;
  ...&lt;br /&gt;
  adcval = ReadChannel(2); /* MUX-Bits auf 0b0010 -&amp;gt; Channel 2 */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekennzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Das Löschen des ADIF-Flags sollte, &#039;&#039;&#039;entgegen&#039;&#039;&#039; der [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_intbits FAQ], mit&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADIF);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
erfolgen. Die Methode in der FAQ eignet sich nur für Register in denen &#039;&#039;&#039;nur&#039;&#039;&#039; Interrupt-Flags stehen.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Analog-Digital-Wandlung ohne internen ADC ===&lt;br /&gt;
&lt;br /&gt;
==== Messen eines Widerstandes ====&lt;br /&gt;
&lt;br /&gt;
Analoge Werte lassen sich ohne Analog-Digital-Wandler auch indirekt ermitteln. Im Folgenden wird die Messung des an einem Potentiometer eingestellten Widerstands anhand der Ladekurve eines Kondensators erläutert. Bei dieser Methode wird nur ein Portpin benötigt, ein Analog-Digital-Wandler oder Analog-Comparator ist nicht erforderlich. Es wird dazu ein Kondensator und der Widerstand (das Potentiometer) in Reihe zwischen Vorsorgungsspannung und Masse/GND geschaltet (sogen. RC-Netzwerk). Zusätzlich wird eine Verbindung der Leitung zwischen Kondensator und Potentiometer zu einem Portpin des Controllers hergestellt. Die folgende Abbildung verdeutlicht die erforderliche Schaltung. &lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wird der Portpin des Controllers auf Ausgang konfiguriert (im Beispiel &#039;&#039;DDRD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) und dieser Ausgang auf Logisch 1 (&amp;quot;High&amp;quot;, &#039;&#039;PORTD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) geschaltet, liegt an beiden &amp;quot;Platten&amp;quot; des Kondensators das gleiche Potential &#039;&#039;&#039;VCC&#039;&#039;&#039; an und der Kondensator somit entladen. (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so, da an beiden Seiten des Kondensators das gleiche Potential anliegt und somit eine Potentialdifferenz von 0V besteht =&amp;gt; Kondensator ist entladen).&lt;br /&gt;
&lt;br /&gt;
Nach einer gewissen Zeit ist der Kondensator entladen und der Portpin wird als Eingang konfiguriert (&#039;&#039;DDRD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2); PORTD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2)&#039;&#039;), wodurch dieser hochohmig wird. Der Status des Eingangspin (in PIND) ist Logisch 1 (High). Der Kondensator lädt sich jetzt über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), wird das Eingangssignal als LOW erkannt (Bit in PIND wird 0). Die Zeitspanne zwischen der Umschaltung von Entladung auf Aufladung und dem Wechsel des Eingangssignals von High auf Low ist ein Maß für den am Potentiometer eingestellten Widerstand. Zur Zeitmessung kann einer der im Controller vorhandenen Timer genutzt werden. Der 220 Ohm Widerstand dient dem Schutz des Controllers. Es würde sonst bei Maximaleinstellung des Potentionmeters (hier 0 Ohm) ein zu hoher Strom fließen, der die Ausgangsstufe des Controllers zerstört. &lt;br /&gt;
&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B: 0...100 % oder so) umzurechnen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Link 404 =&amp;gt; auskommentiert, mthomas 9.2.2008 &lt;br /&gt;
Ein Beispielprogramm findet sich auf [http://www.mypage.bluewin.ch/ch_schifferle/ Christian Schifferles Web-Seite] im Archiv &#039;&#039;ATMEL.ZIP&#039;&#039;, welches unter den Titel &#039;&#039;Tutorial &amp;quot;Programmieren mit C für Atmel Mikrocontroller&#039;&#039; heruntergeladen werden kann. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ADC über Komparator ====&lt;br /&gt;
&lt;br /&gt;
Es gibt einen weiteren Weg, eine analoge Spannung mit Hilfe des&lt;br /&gt;
Komparators, welcher in fast jedem AVR integriert ist, zu messen. Siehe dazu&lt;br /&gt;
auch die Application Note AVR400 von Atmel.&lt;br /&gt;
&lt;br /&gt;
Dabei wird das zu messende Signal auf den invertierenden Eingang&lt;br /&gt;
des Komparators geführt. Zusätzlich wird ein Referenzsignal an den nicht&lt;br /&gt;
invertierenden Eingang des Komparators angeschlossen. Das Referenzsignal wird&lt;br /&gt;
hier auch wieder über ein RC-Glied erzeugt, allerdings mit festen Werten für R&lt;br /&gt;
und C.&lt;br /&gt;
&lt;br /&gt;
[[Image:ADC ueber Komparator.gif]]&lt;br /&gt;
&lt;br /&gt;
Das Prinzip der Messung ist nun dem vorhergehenden recht&lt;br /&gt;
ähnlich. Durch Anlegen eines LOW-Pegels an Pin 2 wird der Kondensator zuerst&lt;br /&gt;
einmal entladen. Auch hier muss darauf geachtet werden, dass der Entladevorgang&lt;br /&gt;
genügend lang dauert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Nun wird Pin 2 auf HIGH gelegt. Der Kondensator wird geladen. Wenn die Spannung&lt;br /&gt;
über dem Kondensator die am Eingangspin anliegende Spannung erreicht hat&lt;br /&gt;
schaltet der Komparator durch. Die Zeit, welche benötigt wird, um den&lt;br /&gt;
Kondensator zu laden kann nun auch wieder als Maß für die Spannung an Pin 1&lt;br /&gt;
herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Ich habe es mir gespart, diese Schaltung auch aufzubauen und&lt;br /&gt;
zwar aus mehreren Gründen:&lt;br /&gt;
&lt;br /&gt;
# 3 Pins notwendig.&lt;br /&gt;
# Genauigkeit vergleichbar mit einfacherer Lösung.&lt;br /&gt;
# War einfach zu faul.&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung liegt allerdings darin, dass damit&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines Digital-Analog-Konverters (&#039;&#039;&#039;DAC&#039;&#039;&#039;) können wir nun auch Analogsignale ausgeben. Es gibt hier mehrere Verfahren. &amp;lt;!-- Wenn wir beim ADC die Möglichkeit haben, mit externen Komponenten zu operieren, müssen wir bei der DAC-Wandlung mit dem auskommen, was der Controller selber zu bieten hat. --mt: hmm, richtig? verstaendlich? redundant? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DAC über mehrere digitale Ausgänge ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir an den Ausgängen des Controllers ein entsprechendes&lt;br /&gt;
Widerstandsnetzwerk aufbauen haben wir die Möglichkeit, durch die Ansteuerung&lt;br /&gt;
der Ausgänge über den Widerständen einen Addierer aufzubauen, mit dessen&lt;br /&gt;
Hilfe wir eine dem Zahlenwert proportionale Spannung erzeugen können. Das&lt;br /&gt;
Schaltbild dazu kann etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
[[Image:DAC R2R.gif]]&lt;br /&gt;
&lt;br /&gt;
Es sollten selbstverständlich möglichst genaue Widerstände verwendet&lt;br /&gt;
werden, also nicht unbedingt solche mit einer Toleranz von 10% oder mehr.&lt;br /&gt;
Weiterhin empfiehlt es sich, je nach Anwendung den Ausgangsstrom über einen&lt;br /&gt;
Operationsverstärker zu verstärken.&lt;br /&gt;
&lt;br /&gt;
=== PWM (Pulsweitenmodulation) ===&lt;br /&gt;
&lt;br /&gt;
Wir kommen nun zu einem Thema, welches in aller Munde ist, aber viele&lt;br /&gt;
Anwender verstehen nicht ganz, wie &#039;&#039;&#039; PWM&#039;&#039;&#039; eigentlich funktioniert.&lt;br /&gt;
&lt;br /&gt;
Wie wir alle wissen, ist ein Mikrocontroller ein rein digitales Bauteil.&lt;br /&gt;
Definieren wir einen Pin als Ausgang, dann können wir diesen Ausgang entweder&lt;br /&gt;
auf HIGH setzen, worauf am Ausgang die Versorgungsspannung &#039;&#039;&#039; Vcc&#039;&#039;&#039; anliegt, oder aber wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt. Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen HIGH und LOW umschalten? - Richtig, wir erhalten eine Rechteckspannung, wie die folgende Abbildung zeigt:&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 1.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Rechteckspannung hat nun einen arithmetischen Mittelwert, &lt;br /&gt;
der je nach Pulsbreite kleiner oder größer ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 2.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun diese pulsierende Ausgangsspannung noch über ein RC-Glied filtern/&amp;quot;glätten&amp;quot;, dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVRs können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. &lt;br /&gt;
Dazu dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt, weshalb ein Blick ins entsprechende Datenblatt dringend angeraten wird.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren, müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| PWM-Modus des Timers ist nicht aktiv.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Timer/Counter zählt nun permanent von 0 bis zur Obergrenze&lt;br /&gt;
und wieder zurück, er wird also als sogenannter Auf-/Ab Zähler betrieben. &lt;br /&gt;
Die Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss mit den Bits &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; desselben&lt;br /&gt;
Registers die gewünschte Ausgabeart des Signals definiert werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Nicht invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Hochzählen und gesetzt beim&lt;br /&gt;
Herunterzählen.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Herunterzählen und gesetzt beim&lt;br /&gt;
Hochzählen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl, um beispielsweise den Timer/Counter als&lt;br /&gt;
nicht invertierenden 10-Bit PWM zu verwenden, heißt dann:&lt;br /&gt;
&lt;br /&gt;
alte Schreibweise (PWMxx wird nicht mehr akzeptiert)&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;PWM11)|(1&amp;lt;&amp;lt;PWM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
neue Schreibweise&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;WGM11)|(1&amp;lt;&amp;lt;WGM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Timer/Counter überhaupt läuft, müssen wir im Control&lt;br /&gt;
Register B &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; noch den gewünschten Takt (Vorteiler) einstellen und&lt;br /&gt;
somit auch die Frequenz des &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signals bestimmen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stop. Der Timer/Counter wird gestoppt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin 1, negative Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin 1, positive Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Also um einen Takt von CK / 1024 zu generieren, verwenden wir&lt;br /&gt;
folgenden Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
TCCR1B = (1&amp;lt;&amp;lt;CS12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt muss nur noch der Vergleichswert festgelegt werden. Diesen&lt;br /&gt;
schreiben wir in das 16-Bit Timer/Counter Output Compare Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem Vergleichswert und dem generierten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal aufzeigen.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
Ach ja, fast hätte ich&#039;s vergessen. Das generierte &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal&lt;br /&gt;
wird am Output Compare Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; des Timers ausgegeben und leider können wir&lt;br /&gt;
deshalb auch beim AT90S2313 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren. Andere AVR-Typen verfügen über bis zu vier PWM-Ausgänge. Zu beachten ist außerdem, das wenn der OC Pin aktiviert ist, er nichtmehr wie üblich funktioniert und z.B. nicht einfach über PINx ausgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Ein Programm, dass an einem ATMega8 den Fast-PWM Modus verwendet, den Modus 14, könnte so aussehen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // OC1A auf Ausgang&lt;br /&gt;
  DDRB = (1 &amp;lt;&amp;lt; PB1 );&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // Timer 1 einstellen&lt;br /&gt;
  //  &lt;br /&gt;
  // Modus 14:&lt;br /&gt;
  //    Fast PWM, Top von ICR1&lt;br /&gt;
  //&lt;br /&gt;
  //     WGM13    WGM12   WGM11    WGM10&lt;br /&gt;
  //      1        1       1        0&lt;br /&gt;
  //&lt;br /&gt;
  //    Timer Vorteiler: 1&lt;br /&gt;
  //     CS12     CS11    CS10&lt;br /&gt;
  //       0        0       1&lt;br /&gt;
  //&lt;br /&gt;
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match&lt;br /&gt;
  //     COM1A1   COM1A0&lt;br /&gt;
  //       1        0&lt;br /&gt;
 &lt;br /&gt;
  TCCR1A = (1&amp;lt;&amp;lt;COM1A1) | (1&amp;lt;&amp;lt;WGM11);&lt;br /&gt;
  TCCR1B = (1&amp;lt;&amp;lt;WGM13) | (1&amp;lt;&amp;lt;WGM12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  //  den Endwert (TOP) für den Zähler setzen&lt;br /&gt;
  //  der Zähler zählt bis zu diesem Wert&lt;br /&gt;
&lt;br /&gt;
  ICR1 = 0x6FFF;&lt;br /&gt;
 &lt;br /&gt;
  //&lt;br /&gt;
  // der Compare Wert&lt;br /&gt;
  // Wenn der Zähler diesen Wert erreicht, wird mit&lt;br /&gt;
  // obiger Konfiguration der OC1A Ausgang abgeschaltet&lt;br /&gt;
  // Sobald der Zähler wieder bei 0 startet, wird der&lt;br /&gt;
  // Ausgang wieder auf 1 gesetzt&lt;br /&gt;
  //&lt;br /&gt;
  // Durch Verändern dieses Wertes, werden die unterschiedlichen&lt;br /&gt;
  // PWM Werte eingestellt.&lt;br /&gt;
&lt;br /&gt;
  OCR1A = 0x3FFF;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  while( 1 )&lt;br /&gt;
    ;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für Details der PWM Möglichkeiten, muß immer das jeweilge Datenblatt des Prozessors konsultiert werden, da sich die unterschiedlichen Prozessoren in ihren Möglichkeiten doch stark unterscheiden. Auch muß man aufpassen, welches zu setzende Bit in welchem Register sind. Auch hier kann es sein, dass gleichnamige Konfigurationsbits in unterschiedlichen Konfigurationsregistern (je nach konkretem Prozessortyp) sitzen.&lt;br /&gt;
&lt;br /&gt;
= LCD-Ansteuerung =&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung an der LCD-Controller-Platine ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; (ist in manchen Unterlagen auch &#039;&#039;&#039;EN&#039;&#039;&#039;  für &#039;&#039;Enable&#039;&#039; abgekürzt) benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert, ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitetet hat (diese Methode u.a. in der LCD-Library von Peter Fleury verwendet). Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt da wir wissen, welche Anschlüsse das LCDs benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin #-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung-LCD&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Pin-µC&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND oder Poti (siehe oben)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD4 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD5 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;offen&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD0 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD1 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD2 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;PD3 am AVR&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man die Steuerleitungen EN und RS auf Pins an einem anderen Port legen möchte, kann man so wie in diesem [http://www.mikrocontroller.net/topic/88543#751982 Forumsbeitrag] vorgehen.&lt;br /&gt;
&lt;br /&gt;
Ok, alles ist verbunden, wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display? &lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
void lcd_data(unsigned char temp1);&lt;br /&gt;
void lcd_string(char *data);&lt;br /&gt;
void lcd_command(unsigned char temp1);&lt;br /&gt;
void lcd_enable(void);&lt;br /&gt;
void lcd_init(void);&lt;br /&gt;
void lcd_home(void);&lt;br /&gt;
void lcd_clear(void);&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y);&lt;br /&gt;
&lt;br /&gt;
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
&lt;br /&gt;
// LCD Befehle&lt;br /&gt;
&lt;br /&gt;
#define CLEAR_DISPLAY 0x01&lt;br /&gt;
#define CURSOR_HOME   0x02&lt;br /&gt;
&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
&lt;br /&gt;
#define LCD_PORT      PORTD&lt;br /&gt;
#define LCD_DDR       DDRD&lt;br /&gt;
#define LCD_RS        PD4&lt;br /&gt;
#define LCD_EN        PD5&lt;br /&gt;
// DB4 bis DB7 des LCD sind mit PD0 bis PD3 des AVR verbunden&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// sendet ein Datenbyte an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_data(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_RS);        // RS auf 1 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_command(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
   unsigned char temp2 = temp1;&lt;br /&gt;
 &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);        // RS auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
   temp1 = temp1 &amp;gt;&amp;gt; 4;              // oberes Nibble holen&lt;br /&gt;
   temp1 = temp1 &amp;amp; 0x0F;            // maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp1;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
 &lt;br /&gt;
   temp2 = temp2 &amp;amp; 0x0F;            // unteres Nibble holen und maskieren&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= temp2;               // setzen&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   &lt;br /&gt;
   _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// erzeugt den Enable-Puls&lt;br /&gt;
void lcd_enable(void)&lt;br /&gt;
{&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
   LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
    _delay_us(1);                   // kurze Pause&lt;br /&gt;
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern&lt;br /&gt;
   // http://www.mikrocontroller.net/topic/80900&lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Initialisierung: &lt;br /&gt;
// Muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
void lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
   LCD_DDR = LCD_DDR | 0x0F | (1&amp;lt;&amp;lt;LCD_RS) | (1&amp;lt;&amp;lt;LCD_EN);   // Port auf Ausgang schalten&lt;br /&gt;
&lt;br /&gt;
   // muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(15);&lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x03;            &lt;br /&gt;
   LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);      // RS auf 0&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4 Bit Modus aktivieren &lt;br /&gt;
   LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
   LCD_PORT |= 0x02;&lt;br /&gt;
   lcd_enable();&lt;br /&gt;
   _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
   // 4Bit / 2 Zeilen / 5x7&lt;br /&gt;
   lcd_command(0x28);&lt;br /&gt;
    &lt;br /&gt;
   // Display ein / Cursor aus / kein Blinken&lt;br /&gt;
   lcd_command(0x0C); &lt;br /&gt;
 &lt;br /&gt;
   // inkrement / kein Scrollen&lt;br /&gt;
   lcd_command(0x06);&lt;br /&gt;
&lt;br /&gt;
   lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
&lt;br /&gt;
void lcd_clear(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CLEAR_DISPLAY);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
&lt;br /&gt;
void lcd_home(void)&lt;br /&gt;
{&lt;br /&gt;
   lcd_command(CURSOR_HOME);&lt;br /&gt;
   _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)&lt;br /&gt;
&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t tmp;&lt;br /&gt;
&lt;br /&gt;
  switch (y) {&lt;br /&gt;
    case 1: tmp=0x80+0x00+x; break;    // 1. Zeile&lt;br /&gt;
    case 2: tmp=0x80+0x40+x; break;    // 2. Zeile&lt;br /&gt;
    case 3: tmp=0x80+0x10+x; break;    // 3. Zeile&lt;br /&gt;
    case 4: tmp=0x80+0x50+x; break;    // 4. Zeile&lt;br /&gt;
  }&lt;br /&gt;
  lcd_command(tmp);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_string(char *data)&lt;br /&gt;
{&lt;br /&gt;
    while(*data) {&lt;br /&gt;
        lcd_data(*data);&lt;br /&gt;
        data++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches die Funktionen benutzt, sieht zb. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen&lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    lcd_data(&#039;T&#039;);&lt;br /&gt;
    lcd_data(&#039;e&#039;);&lt;br /&gt;
    lcd_data(&#039;s&#039;);&lt;br /&gt;
    lcd_data(&#039;t&#039;);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
&lt;br /&gt;
    lcd_string(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist dabei, dass die Optimierung bei der Compilierung eingeschaltet ist, sonst stimmen die Zeiten der Funktionen _delay_us() und _delay_ms() nicht und der Code wird wesentlich länger (Siehe Dokumentation der libc im WinAVR).&lt;br /&gt;
&lt;br /&gt;
Ein Hauptprogramm, welches eine Variable ausgibt, sieht zb. so aus.&lt;br /&gt;
Mittels der itoa() Funktion (itoa = &amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;nteger &amp;lt;b&amp;gt;To&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;A&amp;lt;/b&amp;gt;scii ) wird von einem Zahlenwert eine textuelle Repräsentierung ermittelt (sprich: ein String erzeugt) und dieser String mit der bereits vorhandenen Funktion lcd_string ausgegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen &lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Beispiel&lt;br /&gt;
int variable = 42;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe des Zeichens dessen ASCII-Code gleich dem Variablenwert ist&lt;br /&gt;
    // (Im Beispiel entspricht der ASCII-Code 42 dem Zeichen *)&lt;br /&gt;
    // http://www.code-knacker.de/ascii.htm&lt;br /&gt;
    lcd_data(variable);&lt;br /&gt;
&lt;br /&gt;
    set_cursor(0,2);&lt;br /&gt;
 &lt;br /&gt;
    // Ausgabe der Variable als Text in dezimaler Schreibweise&lt;br /&gt;
    {&lt;br /&gt;
       // ... umwandeln siehe FAQ Artikel bei www.mikrocontroller.net&lt;br /&gt;
       // WinAVR hat eine itoa()-Funktion, das erfordert obiges #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
       char Buffer[20]; // in diesem {} lokal&lt;br /&gt;
       itoa( variable, Buffer, 10 ); &lt;br /&gt;
&lt;br /&gt;
       // ... ausgeben  &lt;br /&gt;
       lcd_string( Buffer );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Einrichten eines Projekts muss man zu der Datei mit dem Hauptprogramm auch die Datei lcd-routines.c in das Projekt aufnehmen. Dies geschieht beim AVR Studio unter Source Files im Fenster AVR GCC oder bei WinAVR im Makefile (z.B. durch SRC += lcd-routines.c).&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVRs sind für viele Steuerungsaufgaben zu schnell. Wenn wir beispielsweise eine LED oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die CPU-Frequenz verwenden, da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, Vorgänge in Zeitabständen durchzuführen, die geringer als die Taktfrequenz des Controllers sind. Selbstverständlich sollte die resultierende Frequenz auch noch möglichst genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist ganz einfach ein bestimmtes Register im µC, das völlig ohne Zutun des Programms, also per Hardware, hochgezählt wird. Das alleine wäre noch nicht allzu nützlich, wenn nicht dieses Hardwareregister bei bestimmten Zählerständen einen Interrupt auslösen könnte. Ein solches Ereignis ist der Overflow: Da die Bitbreite des Registers beschränkt ist, kommt es natürlich auch vor, dass der Zähler so hoch zählt, dass der nächste Zählerstand mit dieser Bitbreite nicht mehr darstellbar ist und der Zähler wieder auf 0 zurückgesetzt wird. Dieses Ereignis nennt man den Overflow und es ist möglich an dieses Ereignis einen Interrupt zu koppeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Alternativvorschlag mthomas &lt;br /&gt;
Jeder Timer verfügt über ein Zählerregister im Mikrocontroller, das automatisch und ohne Zutun des Programms von der Hardware weitergezählt wird. In einem einfachen Anwendungsfall stellt man den Timer auf eine Zählgeschwindigkeit (Frequenz) und kann dann anhand des Zählerstands ermitteln, wie viel Zeit vergangen ist. Das eigentlich Nützliche an Timern ist jedoch, dass man  bestimmte Zählerstände mit Interrupts verknüpfen kann, so dass der Controller beim Auftreten automatisch eine vom Anwender geschriebene Routine aufruft. Eines dieser möglichen Ereignis ist der Overflow ((Zähler-)Überlauf), der dann auftritt, wenn der Wert des Zählerregisters (Timer/Counter-Register) den maximal möglichen Wert überschreitet. Der Maximalwert wird durch die Bitbreite des Zählerregisters bestimmt (z.B. 255 bei 8-Bit Timern). Beim Überlauf/Overflow wird der Zähler durch die Hardware auf 0 zurückgesetzt und die Zählung beginnt von neuem. Wurde vorher der Overflow-Interrupt für den Timer aktiviert (im Timer Control Register) unterbricht der Controller automatisch die Ausführung des Hauptprogramms und verzweigt in die Interrupt-Routine des Anwenders.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auf den AT90S2313. Für andere Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer Auflösung von 65536. Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz, der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
== Der Vorteiler (Prescaler) ==&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler dient dazu, den CPU-Takt vorerst um einen einstellbaren Faktor zu reduzieren. Die so geteilte Frequenz wird den Eingängen der Timer zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024 einstellen, wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca. 4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw. Zählregister (TCNTx) mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle verfügen über mindestens einen, teilweise sogar zwei, 8-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird z.B bei AT90S2313 über folgende Register angesprochen (bei anderen Typen weitestgehend analog):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    TCCR0 |= (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst und die daran gebundene Interrupt-Routine abgearbeitet. Das TOV Flag &lt;br /&gt;
lässt sich durch das Hineinschreiben einer 1 und nicht wie erwartet einer 0 wieder zurücksetzen.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen außer den 8-Bit Timern auch 16-Bit Timer. Die 16-Bit Timer/Counter sind etwas komplexer aufgebaut als die 8-Bit Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 8-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| 9-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM13&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WGM12 (CTC1)&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorteiler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt, und C der voreingestellte Vergleichswert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stopp, Der Timer/Counter wird angehalten.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CPU-Takt / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CPU-Takt / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin T1, fallende Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin T1, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T1 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T1 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0, bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;TCNT1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;OCR1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;OCR1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;OCR1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;OCR1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäß folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]] [[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Ausserdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr. Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen kleiner 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4us warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.6 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t x;&lt;br /&gt;
&lt;br /&gt;
    x = 10;&lt;br /&gt;
&lt;br /&gt;
    while (x &amp;gt;= 0)&lt;br /&gt;
    {&lt;br /&gt;
      // tu was&lt;br /&gt;
&lt;br /&gt;
      x--;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog? ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Einige Controller haben einen eigenen Watchdog Oszillator, z.B. der Tiny2313 mit 128kHz. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde, beginnt der Counter von 0 an hochzuzählen. &lt;br /&gt;
Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern, müssen wir den Watchdog regelmäßig wieder neu starten bzw. rücksetzen (Watchdog Reset). &lt;br /&gt;
Dies sollte innerhalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern, muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten. Es müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. &lt;br /&gt;
Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist, wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei &#039;&#039;wdt.h&#039;&#039; (&#039;&#039;#include &amp;lt;avr/wdt.h&amp;gt;&#039;&#039;) in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch gestartet wird. &lt;br /&gt;
Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. &lt;br /&gt;
Falls ein anderer Wert gewünscht ist, so kann dies im Makfile in den Linker-Optionen eingetragen werden. &lt;br /&gt;
Dazu muss in der Zeile LDFLAGS folgende Option angefügt werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert&lt;br /&gt;
:Mögliche Timeoutwerte:&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Konstante&lt;br /&gt;
! Wert&lt;br /&gt;
! TimeOut&lt;br /&gt;
|- &lt;br /&gt;
| WDTO_15MS   &lt;br /&gt;
| 0&lt;br /&gt;
| 15 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_30MS   &lt;br /&gt;
| 1&lt;br /&gt;
| 30 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_60MS   &lt;br /&gt;
| 2&lt;br /&gt;
| 60 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_120MS   &lt;br /&gt;
| 3&lt;br /&gt;
| 120 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_250MS   &lt;br /&gt;
| 4&lt;br /&gt;
| 250 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_500MS   &lt;br /&gt;
| 5&lt;br /&gt;
| 500 ms&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_1S   &lt;br /&gt;
| 6&lt;br /&gt;
| 1 S&lt;br /&gt;
|-&lt;br /&gt;
| WDTO_2S   &lt;br /&gt;
| 7&lt;br /&gt;
| 2s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
== Watchdog-Anwendungshinweise ==&lt;br /&gt;
&lt;br /&gt;
Ob nun der Watchdog als Schutzfunktion überhaupt verwendet werden soll, hängt stark von der Anwendung, der genutzten Peripherie und dem Umfang und der Qualitätssicherung des Codes ab. Will man sicher gehen, dass ein Programm sich nicht in einer Endlosschleife verfängt, ist der Wachdog das geeignete Mittel dies zu verhindern. Weiterhin kann bei geschickter Programmierung der Watchdog dazu genutzt werden, bestimmte Stromsparfunktionen zu implementieren. Bei einigen neueren AVRs (z.B. dem ATTiny13) kann der Watchdog auch direkt als Timer genutzt werden, der den Controller aus einem Schlafmodus aufweckt. Auch dies kann im &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register eingestellt werden. Außerdem bietet der WD die einzige Möglichkeit einen beabsichtigten System-Reset (ein &amp;quot;richtiger Reset&amp;quot;, kein &amp;quot;jmp 0x0000&amp;quot;) ohne externe Beschaltung auszulösen, was z.B. bei der Implementierung eines Bootloaders nützlich ist. Bei bestimmten Anwendungen kann die Nutzung des WD als &amp;quot;ultimative Deadlock-Sicherung für nicht bedachte Zustände&amp;quot; natürlich immer als zusätzliche Sicherung dienen. &lt;br /&gt;
&lt;br /&gt;
Es besteht die Möglichkeit herauszufinden, ob ein Reset durch den Watchdog ausgelöst wurde (beim ATmega16 z.B. Bit WDRF in MCUCSR). Diese Information sollte auch genutzt werden, falls ein WD-Reset in der Anwendung nicht planmäßig implementiert wurde. Zum Beispiel kann man eine LED an einen freien Pin hängen, die nur bei einem Reset durch den WD aufleuchtet oder aber das &amp;quot;Ereignis WD-Reset&amp;quot; im internen EEPROM des AVR absichern, um die Information später z.B. über UART oder ein Display auszugeben (oder einfach den EEPROM-Inhalt über die ISP/JTAG-Schnittstelle auslesen).&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/77273#642501 Bug in ATtiny2313?]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt der Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
In der Beschreibung heißt es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft. Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe Abschnitt (TODO: verlinken) im Anhang.&lt;br /&gt;
&amp;lt;!--Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. vgl. [[AVR-GCC-Tutorial#Anhang|Anhang]])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.B. &#039;&#039;void TIMER0_OVF_vect(void)...&#039;&#039;). Der Unterschied im Vergleich zu ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben.  &amp;lt;!--Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)), als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur &lt;br /&gt;
in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechnungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechnungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0, PORTA ist danach 0b00000001. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-oder-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8 (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten &#039;&#039;Sleep-Modes&#039;&#039; (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_enable()&#039;&#039;&#039;&lt;br /&gt;
:aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_cpu()&#039;&#039;&#039;&lt;br /&gt;
: Versetzt den Controller in den Schlafmodus (sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt)&lt;br /&gt;
* &#039;&#039;&#039;sleep_disable()&#039;&#039;&#039;&lt;br /&gt;
:deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts.&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;allozieren&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich, um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory, bei AVR Controllern mit mehr als 64kB Flash auch elpm). Die Standard-Laufzeitbibliothek des avr-gcc (die avr-libc) stellt diese Funktionen nach Einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Lokale Variablen (eigentlich Konstanten) innerhalb von Funktionen können ebenfalls im Programmspeicher abgelegt werden. Dazu ist bei der Definition jedoch ein &#039;&#039;static&#039;&#039; voranzustellen, da solche &amp;quot;Variablen&amp;quot; nicht auf dem Stack bzw. (bei Optimierung) in Registern verwaltet werden können. Der Compiler &amp;quot;wirft&amp;quot; eine Warnung falls static fehlt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray2 };&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
  static /*const*/ uint8_t pgmTestByteLocal PROGMEM = 0x55;&lt;br /&gt;
  static /*const*/ char pgmTestStringLocal[] PROGMEM = &amp;quot;im Flash&amp;quot;;&lt;br /&gt;
  // so nicht (static fehlt): char pgmTestStringLocalFalsch [] PROGMEM = &amp;quot;so nicht&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 // ...&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
    //...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte....&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWort);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;groß&amp;quot; (Stand avr-gcc 3.4.x). Damit ist der mögliche Speicherbereich für &amp;quot;Flash-Konstanten&amp;quot; auf 64kB begrenzt.  &amp;lt;!-- Einige avr-libc/pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher) (intern via Assembler Anweisung ELPM). Die Initialisierungswerde des Speicherinhalts jenseits der 64kB-Marke müssen dann jedoch auf anderem Weg angelegt werden (nicht PROGMEM, evtl. eigene Section und Linker-Optionen - TODO) /// alt - und nicht ganz korrekt: (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.)--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray enthält nun die Startadresse des Byte-Arrays pgmFooByteArray1&lt;br /&gt;
    // Allerdings würde ein direkter Zugriff mit diesem Pointer (z.B. temp=*ptrToArray)&lt;br /&gt;
    // &#039;&#039;&#039;nicht&#039;&#039;&#039; den Inhalt von pgmFooByteArray1[0] liefern, sondern von einer Speicherstelle&lt;br /&gt;
    // im &#039;&#039;&#039;RAM&#039;&#039;&#039;, die die gleiche Adresse hat wie pgmFooByteArray1[0]&lt;br /&gt;
    // Daher muss nun die Funktion pgm_read_byte() benutzt werden, die die in ptrToArray&lt;br /&gt;
    // enthaltene Adresse benutzt und auf das Flash zugreift.&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray enthält nun die Adresse des ersten Elements des Byte-Arrays pgmFooByteArray2&lt;br /&gt;
    // da im zweiten Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmFooByteArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Array aus Zeichenketten im Flash-Speicher ===&lt;br /&gt;
&lt;br /&gt;
Felder aus Zeichenketten im Flash-Speicher werden in zwei Schritten angelegt: Zuerst die einzelnen Elemente des Arrays und im Anschluss ein Array, in dem die Addressen der Zeichenketten abgelegt werden. Zum Auslesen wird zuerst die Adresse des i-ten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, auf das Element (die Zeichenkette) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const char str1[] PROGMEM = &amp;quot;first_A&amp;quot;;&lt;br /&gt;
const char str2[] PROGMEM = &amp;quot;second_A&amp;quot;;&lt;br /&gt;
const char str3[] PROGMEM = &amp;quot;third_A&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char *strarray1[] PROGMEM = {&lt;br /&gt;
	str1,&lt;br /&gt;
	str2,&lt;br /&gt;
	str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	int i, j, l;&lt;br /&gt;
	const char *pstrflash;&lt;br /&gt;
	char work[20], work2[20];&lt;br /&gt;
	// fuer Simulation: per volatile Optimierung verhindern, &lt;br /&gt;
	//                  da c nicht genutzt&lt;br /&gt;
	volatile char c;&lt;br /&gt;
	&lt;br /&gt;
	for ( i = 0; i &amp;lt; (sizeof(strarray1)/sizeof(strarray1[0]) ); i++ ) {&lt;br /&gt;
&lt;br /&gt;
		// setze Pointer auf die Addresse des i-ten Elements des&lt;br /&gt;
		// &amp;quot;Flash-Arrays&amp;quot; (str1, str2, ...)&lt;br /&gt;
		pstrflash = (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) );&lt;br /&gt;
		&lt;br /&gt;
		// kopiere den Inhalt der Zeichenkette von der&lt;br /&gt;
		// in pstrflash abgelegten Adresse in das work-Array&lt;br /&gt;
		// analog zu strcpy( work, strarray1[i]) wenn alles im RAM&lt;br /&gt;
		strcpy_P( work, pstrflash );&lt;br /&gt;
		// verkuerzt:&lt;br /&gt;
		strcpy_P( work2, (const char*)( pgm_read_word( &amp;amp;(strarray1[i]) ) ) );&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
		// Zeichen-fuer-Zeichen&lt;br /&gt;
		l = strlen_P( pstrflash );&lt;br /&gt;
		for ( j=0; j &amp;lt; l; j++ ) {&lt;br /&gt;
			// analog zu c=strarray[i][j] wenn alles im RAM&lt;br /&gt;
			c = (char)( pgm_read_byte( pstrflash++ ) );&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	while (1) { ; }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: &amp;quot;How do I put an array of strings completely in ROM?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5)) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Vorsicht: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char textImFlashOK[] PROGMEM = &amp;quot;mit[]&amp;quot;; &lt;br /&gt;
// = Daten im &amp;quot;Flash&amp;quot;, textImFlashOK* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const char* textImFlashProblem PROGMEM = &amp;quot;mit*&amp;quot;;&lt;br /&gt;
// Konflikt: Daten im BSS (lies: RAM), textImFlashFAIL* zeigt auf Flashadresse&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der Initialisierungsstring von &amp;quot;textImFlashProblem&amp;quot; zu den Konstanten ans Ende des Programmcodes gelegt wird (BSS), von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte (und wird). Da der lesende Code (mittels pgm_read*) trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein weiters Problem des AVR-GCC (gesehen bei avr-gcc 3.4.1 und 3.4.2) bei der Anpassung an die Harvard-Architektur zu sein (konstanter Pointer auf variable Daten?!). Abhilfe (&amp;quot;Workaround&amp;quot;): Initialisierung bei Zeichenketten mit [] oder gleich im Code PSTR(&amp;quot;...&amp;quot;) nutzen.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden, ob es sich um eine Adresse im Flash oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind.&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die einen im Flash abgelegten String z.B. an eine UART ausgibt, würde dann so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void uart_puts_p(const char *text)&lt;br /&gt;
{&lt;br /&gt;
    char Zeichen;&lt;br /&gt;
&lt;br /&gt;
    while (Zeichen = pgm_read_byte(text))&lt;br /&gt;
    {   /* so lange, wie mittels pgm_read_byte ein Zeichen vom Flash gelesen&lt;br /&gt;
           werden konnte, welches nicht das &amp;quot;String-Endezeichen&amp;quot; darstellt */&lt;br /&gt;
&lt;br /&gt;
        /* Das gelesene Zeichen über die normalen Kanäle verschicken */&lt;br /&gt;
        uart_putc(Zeichen);&lt;br /&gt;
        text++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Flash in der Anwendung schreiben ===&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option (auch bekannt als Bootloader-Support) können Teile des Flash-Speichers auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. Bei wenigen &amp;quot;kleinen&amp;quot; AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm(Flash)- und Datenspeicher(RAM) auf. Der C-Standard und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiß&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, dass vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgegeben sind, die innerhalb weniger Taktzyklen aufeinanderfolgen müssen (&amp;quot;timed sequence&amp;quot;). Auch dies muss bei Nutzung der Funktionen aus der avr-libc/eeprom.h-Datei nicht selbst implementiert werden. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Siehe dazu folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt; // wird in aktuellen Versionen der avr-lib mit xx.h eingebunden&lt;br /&gt;
&lt;br /&gt;
// EEMEM wird bei aktuellen Versionen der avr-lib in eeprom.h definiert&lt;br /&gt;
// hier: definiere falls noch nicht bekannt (&amp;quot;alte&amp;quot; avr-libc)&lt;br /&gt;
#ifndef EEMEM&lt;br /&gt;
// alle Textstellen EEMEM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEMEM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
//...&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // &#039;Variablen&#039; eeFooByte geschrieben&lt;br /&gt;
//...&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z.B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    #define EEPROM_DEF 0xFF&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ( ( myByte = eeprom_read_byte(&amp;amp;eeFooByte) ) == EEPROM_DEF ) {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
//...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprüft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und möglicherweise noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
    uint8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,3);&lt;br /&gt;
&lt;br /&gt;
    /* dito etwas anschaulicher aber &amp;quot;unnütze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray1,sizeof(myWordBuffer));&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fließkommazahlen lassen sich recht praktisch über eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t i[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    uint8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEMEM;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
//...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklaration abgeleiteten EEPROM-Inhalt in eine Datei geschrieben werden (übliche Dateiendung: .eep, Daten im Intel Hex-Format). Damit können recht elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen (siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles). Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden (Write EEPROM), wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse (vgl. Datenblatt Abschnitt Fuse Bits) die vorherigen Daten (EESAVE programmed = 0), deren Position möglicherweise nicht mehr mit der Belegung im aktuellen Programm übereinstimmt oder den Standardwert nach &amp;quot;Chip Erase&amp;quot;: 0xFF (EESAVE unprogrammed = 1). Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Eine besondere Funktion des avr-gcc ist, dass mit entsprechenden Optionen im Makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Variable auf feste Adressen legen ===&lt;br /&gt;
&lt;br /&gt;
Gleich zu Beginn möchte ich darauf hinweisen, dass dieses Verfahren nur ein Workaround ist, mit dem man das Problem der anscheinend &amp;quot;zufälligen&amp;quot; Verteilung&lt;br /&gt;
der EEPROM-Variablen durch den Compiler etwas in den Griff bekommen kann.&lt;br /&gt;
&lt;br /&gt;
Hilfreich kann dies vor allem dann sein, wenn man z.B. über einen Kommandointerpreter (o.ä. Funktionen) direkt bestimmte EEPROM-Adressen manipulieren möchte. Auch wenn man über einen JTAG-Adapter (mk I oder mkII) den Programmablauf manipulieren möchte, indem man die EEPROM-Werte direkt ändert, kann diese Technik hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Im folgenden nun zwei Sourcelistings mit einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.h&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#inlcude &amp;lt;avr/eeprom.h&amp;gt;     // Die EEPROM-Definitionen/Macros der avr-libc einbinden&lt;br /&gt;
&lt;br /&gt;
#define   EESIZE   512      // Maximale Größe des EEPROMS&lt;br /&gt;
&lt;br /&gt;
#define   EE_DUMMY   0x000  // Dummyelement (Adresse 0 sollte nicht genutzt werden)&lt;br /&gt;
#define   EE_VALUE1  0x001  // Eine Bytevariable  &lt;br /&gt;
#define   EE_WORD1L  0x002  // Eine Wordvariable (Lowbyte)&lt;br /&gt;
#define   EE_WORD1H  0x003  // Eine Wordvariable (Highbyte)&lt;br /&gt;
#define   EE_VALUE2  0x004  // Eine weitere Bytevariable&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den Macros &#039;&#039;&#039;#define EE_VALUE1&#039;&#039;&#039; legt man den Namen und die Adresse der&lt;br /&gt;
&#039;Variablen&#039; fest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Die Adressen sollten fortlaufend, zumindest aber aufsteigend sortiert sein! Ansonsten besteht die Gefahr, daß man sehr schnell ein Durcheinander im EEPROM Speicher veranstaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=$FF0000&amp;gt;WICHTIG:&amp;lt;/font&amp;gt;Für den Compiler sind das lediglich Speicher-Adressen, über die auf das EEPROM zugegriffen wird. Der Compiler sieht nichts davon als eine echte Variable an und stößt sich daher auch nicht daran, wenn 2 Makros mit der gleichen Speicheradresse, bzw. überlappenden Speicherbereichen definiert werden. Es liegt einzig und alleine in der Hand des Programmierers, hier keinen Fehler zu machen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
   [EE_DUMMY]   = 0x00,&lt;br /&gt;
   [EE_VALUE1]  = 0x05,&lt;br /&gt;
   [EE_WORD1L]  = 0x01,   &lt;br /&gt;
   [EE_WORD1H]  = 0x00,&lt;br /&gt;
   [EE_VALUE2]  = 0xFF&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch die Verwendung eines Array, welches das gesammte EEPROM umfasst, bleibt&lt;br /&gt;
dem Compiler nicht anderes übrig, als das Array so zu platzieren, dass Element 0&lt;br /&gt;
des Arrays der Adresse 0 des EEPROMs entspricht. (&#039;&#039;Ich hoffe nur, dass die Compilerbauer daran nichts ändern!&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Wie man in dem obigen Codelisting auch sehen kann, hat das Verfahren einen kleinen Haken. Variablen die größer sind als 1 Byte, müssen etwas umständlicher&lt;br /&gt;
definiert werden. Benötigt man keine Initialisierung durch das Programm (was der Normalfall sein dürfte), dann kann man das auch so machen:&lt;br /&gt;
&lt;br /&gt;
Möchte man im EEPROM hintereinander beispielsweise Variablen, mit den Namen &#039;&#039;&#039;Wert&#039;&#039;&#039;, &#039;&#039;&#039;Anzahl&#039;&#039;&#039;, &#039;&#039;&#039;Name&#039;&#039;&#039; und &#039;&#039;&#039;Wertigkeit&#039;&#039;&#039; definieren, wobei Wert und Wertigkeit 1 Byte belegen sollen, Anzahl als 1 Wort (also 2 Bytes) und Name mit 10 Bytes reserviert werden soll, so geht auch folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jedes Makro definiert also seine Startadresse durch die Startadresse der unmittelbar vorhergehende &#039;Variablen&#039; plus der Anzahl der Bytes die von der vorhergehenden &#039;Variablen&#039; verbraucht werden. Dadurch ist man zumindest etwas auf der sicheren Seite, dass keine 2 &#039;Variablen&#039; im EEPROM überlappend definiert werden. Möchte man eine weitere &#039;Variable&#039; hinzufügen, so wird deren&lt;br /&gt;
Name, einfach anstelle der EE_LAST eingesetzt und eine neue Zeile für EE_LAST eingefügt, in der dann die Größe der &#039;Variablen&#039; festgelegt wird. Zb.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define EE_DUMMY      0x000&lt;br /&gt;
#define EE_WERT       ( 0x000 + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_ANZAHL     ( EE_WERT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_NAME       ( EE_ANZAHL + sizeof( uint16_t ) )&lt;br /&gt;
#define EE_WERTIGKEIT ( EE_NAME + 10 * sizeof( uint8_t ) )&lt;br /&gt;
#define EE_PROZENT    ( EE_WERTIGKEIT + sizeof( uint8_t ) )&lt;br /&gt;
#define EE_LAST       ( EE_WERTIGKEIT + sizeof( double ) )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EE_PROZENT legt die Startadresse für eine neue &#039;Variable&#039; des Datentyps double fest.&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf die EEPROM Werte kann dann z.B.so erfolgen:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t   temp1;&lt;br /&gt;
uint16_t  temp2;&lt;br /&gt;
&lt;br /&gt;
temp1 = eeprom_read_byte(EE_VALUE1);&lt;br /&gt;
temp2 = eeprom_read_word(EE_WORD1L);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ob die in der avr-libc vorhandenen Funktionen dafür verwendet werden können, weiß ich nicht. Aber in einigen Fällen muss man sich sowieso eigene Funktionen&lt;br /&gt;
bauen, welche die spezifischen Anforderungen (Interrupt - Atom Problem, etc.)&lt;br /&gt;
erfüllen.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist nur eine Möglichkeit, wie man dies realisieren kann. Sie bietet einem eine relativ einfache Art die EEPROM-Werte&lt;br /&gt;
auf beliebige Adressen zu legen oder Adressen zu ändern. Die Andere Möglichkeit besteht darin, die EEPROM-Werte wie folgt zu belegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
// Datei &amp;quot;eeprom.c&amp;quot; eines eigenen Projektes&lt;br /&gt;
/////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;eeprom.h&amp;quot;          // Eigene EEPROM-Headerdatei einbinden&lt;br /&gt;
&lt;br /&gt;
uint8_t ee_mem[EESIZE] EEMEM =&lt;br /&gt;
{&lt;br /&gt;
  0x00,                     //  ee_dummy&lt;br /&gt;
  0x05,                     //  ee_value1&lt;br /&gt;
  0x01,                     //  ee_word1L&lt;br /&gt;
  0x00,                     // (ee_word1H)&lt;br /&gt;
  0xFF                      //  ee_value2&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei kann man Variablen, die größer sind als 1 Byte einfacher definieren und&lt;br /&gt;
man muss nur die Highbyte- oder Lowbyte-Adresse in der &amp;quot;eeprom.h&amp;quot; definieren.&lt;br /&gt;
Allerdings muss man hier höllisch aufpassen, dass man nicht um eine oder mehrere&lt;br /&gt;
Positionen verrutscht!&lt;br /&gt;
&lt;br /&gt;
Welche der beiden Möglichkeiten man einsetzt, hängt vor allem davon ab, wieviele&lt;br /&gt;
Byte, Word und sonstige Variablen man benutzt. Gewöhnen sollte man sich an beide Varianten können ;)&lt;br /&gt;
&lt;br /&gt;
Kleine Schlussbemerkung:&lt;br /&gt;
&lt;br /&gt;
* Der avr-gcc unterstützt die Variante 1 und die Variante 2&lt;br /&gt;
* Der icc-avr Compiler unterstützt nur die Variante 2!&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Bei alten Versionen der avr-libc wurden nicht alle AVR Controller  untersützt. Z.B. bei der avr-libc Version 1.2.3 insbesondere bei AVRs &amp;quot;der neuen Generation&amp;quot; (ATmega48/88/168/169) funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register). In neueren Versionen (z.B. avr-libc 1.4.3 aus WinAVR 20050125) wurde die Zahl der unterstüzten Controller deutlich erweitert und eine Methode zur leichten Anpassung an zukünftige Controller eingeführt.&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Will oder kann man nicht auf die neue Version aktualisieren, kann der dort gezeigte Code auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;, gegebenfalls Schutz vor Unterbrechnung/Interrupt ergänzen &#039;&#039;uint8_t sreg; sreg=SREG; cli(); [EEPROM-Code] ; SREG=sreg; return;&#039;&#039;, siehe Abschnitt Interrupts). Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== EEPROM Register ===&lt;br /&gt;
Um das EEPROM anzusteuern sind drei Register von Bedeutung.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEAR Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL da in einem 8 Bit Register keine 512 Adressen adressiert werden können&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EEDR Hier werden die Daten eingetragen die geschrieben werden sollen bzw. es enthält die gelesenen Daten&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;LI&amp;gt; EECR Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;TABLE BORDER=1&amp;gt;&lt;br /&gt;
&amp;lt;TR&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;Bit&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;1&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;0&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&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;Name&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;&amp;amp;nbsp;&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;TD&amp;gt;-&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERIE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEMWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EEWE&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;EERE&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;Read/Write&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;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&amp;lt;/TD&amp;gt;&lt;br /&gt;
  &amp;lt;TD&amp;gt;R/W&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;Init Value&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;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;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;TD&amp;gt;0&amp;lt;/TD&amp;gt;&lt;br /&gt;
&amp;lt;/TR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/TABLE&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bedeutung der Bits:&amp;lt;/U&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
Bit 4-7 nicht belegt.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 3 (EERIE) - EEPROM Ready Interrupt Enable:&amp;lt;/U&amp;gt; Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7) wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0 wird kein Interrupt ausgelöst.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 2(EEMWE) - EEPROM Master Write Enable:&amp;lt;/U&amp;gt; Dieses Bit bestimmt, daß wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE =0 ist und EEWE = 1 gesetzt wird hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst.&lt;br /&gt;
Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 1 (EEWE) - EEPROM Write Enable:&amp;lt;/U&amp;gt; Dieses Bit löst den Schreibvorgang aus wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist wird dieses Bit automatisch wieder auf 0 gesetzt und sofern EERIE gesetzt ist ein Interrupt ausgelöst. &amp;lt;BR&amp;gt;&lt;br /&gt;
Ein Schreibvorgang sieht typischerweise wie folgt aus:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. EEPROM Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Daten übergeben an EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1 &amp;lt;BR&amp;gt;&lt;br /&gt;
5. (Optinal) Warten bis Schreibvorgang abgeschlossen ist.&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;U&amp;gt;Bit 0 EERE – EEPROM Read Enable:&amp;lt;/U&amp;gt; Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit wenn das Bit EEWE=0 ist. ISt der LEsevorgang abgeschlossen wird das Bit wieder auf 0 gesetzt und das EEPROM ist für neue Lese/Schreibbefehle wieder bereit.&amp;lt;BR&amp;gt;&lt;br /&gt;
Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&amp;lt;BR&amp;gt;&lt;br /&gt;
1. Bereitschaft zum lesen prüfen (EEWE=0)&amp;lt;BR&amp;gt;&lt;br /&gt;
2. Adresse übergeben an EEAR&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Lesezyklus auslösen mit EERE = 1&amp;lt;BR&amp;gt;&lt;br /&gt;
4. Warten bis Lesevorgang abgeschlossen EERE = 0&amp;lt;BR&amp;gt;&lt;br /&gt;
5. Daten abholen aus EEDR&amp;lt;BR&amp;gt;&lt;br /&gt;
&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bietet sich printf/sprintf an. &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t s[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( s, &amp;quot;Zählerstand: %d&amp;quot;, value );&lt;br /&gt;
    uart_puts( s );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, die Ausgabe stdout auf eigene Ausgabefunktionen umzuleiten. Dazu wird dem Ausgabemechanismus eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben (wie auch immer die Ausgabe stattfindet). Alle anderen, höheren, Funktionen greifen letztendlich auf diese eine Ausgabefunktion zurück, die die Ausgabe eines einzelnen Zeichens vornimmt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uart_init();&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
    printf( &amp;quot;Hello, world!\n&amp;quot; );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil aller printf-Varianten: Sie sind recht speicherintensiv.&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C- und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gnu-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Inline-Assembler: Die Assembleranweisungen werden direkt in den C-Code integriert. Eine Quellcode-Datei enthält somit C- und Assembleranweisungen&lt;br /&gt;
* Assembler-Dateien: Der Assemblercode befindet sich in eigenen Quellcodedateien. Diese werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung (NOP steht für No Operation). Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=Warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
   /* Verzögern der weiteren Programmausführung um&lt;br /&gt;
      genau 3 Taktzyklen */&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    asm volatile (&amp;quot;nop&amp;quot;);&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeintlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
&lt;br /&gt;
    /* leere Schleife - wird bei eingeschalteter Compiler-Optimierung   wegoptimiert */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      ;&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
    for (i = 0; i &amp;lt; 1000; i++)&lt;br /&gt;
      asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    /* alternative Methode (keine Optimierung): */&lt;br /&gt;
    volatile uint16_t j;&lt;br /&gt;
    for (j = 0; j &amp;lt; 1000; j++)&lt;br /&gt;
      ;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützlicher &amp;quot;Assembler-Einzeiler&amp;quot; ist der Aufruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;);&#039;&#039;), da hierzu in älteren Versionen der avr-libc keine eigene Funktion existiert (in neueren Versionen &#039;&#039;sleep_cpu()&#039;&#039; aus sleep.h).&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise Delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil, dass die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static inline void delayloop16 (uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
    asm volatile (&amp;quot;cp  %A0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;cpc %B0, __zero_reg__ \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;breq 2f               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;1:                    \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;sbiw %0,1             \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;brne 1b               \n\t&amp;quot;&lt;br /&gt;
                  &amp;quot;2:                    &amp;quot;  &lt;br /&gt;
                  : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
	          : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
    );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Anweisung wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen. Der Zeilenumbruch teilt dem Assembler mit, dass ein neuer Befehl beginnt.&lt;br /&gt;
* Als Sprung-Marken (Labels) werden Ziffern verwendet. Diese speziellen Labels sind mehrfach im Code verwendbar. Gesprungen wird jeweils zurück (b) oder vorwärts (f) zum nächsten ausffindbaren Label.&lt;br /&gt;
&lt;br /&gt;
Das Resultat zeigt ein Blick in die Assembler-Datei, die der Compiler mit der option &amp;lt;tt&amp;gt;-save-temps&amp;lt;/tt&amp;gt; nicht löscht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	cp  r24, __zero_reg__ 	 ;  count&lt;br /&gt;
	cpc r25, __zero_reg__ 	 ;  count&lt;br /&gt;
	breq 2f               &lt;br /&gt;
	1:                    &lt;br /&gt;
	sbiw r24,1             	 ;  count&lt;br /&gt;
	brne 1b               &lt;br /&gt;
	2:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc im Abschnitt [http://www.nongnu.org/avr-libc/user-manual/inline_asm.html Related Pages/Inline Asm]. &lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc Deutsche Einführung in Inline-Assembler]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
Wenn man mit dem AVR Studio arbeitet, kann alternativ auch das standardmäßig erstellte Makefile bearbeitet und folgende Zeilen eingefügt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
## Objects that must be built in order to link&lt;br /&gt;
OBJECTS = (alte Dateien...) useful.o&lt;br /&gt;
&lt;br /&gt;
## Compile&lt;br /&gt;
## Hier folgt eine Liste der gelinkten Dateien, darunter einfügen:&lt;br /&gt;
useful.o: ../useful.S&lt;br /&gt;
	$(CC) $(INCLUDES) $(ASMFLAGS) -c  $&amp;lt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Das war es schon. Allerdings gilt es zu beachten, dass das makefile über &amp;quot;Project -&amp;gt; Configuration options&amp;quot; selbst einzubinden ist, sonst wird es natürlich wieder überschrieben.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel eine Funktion &#039;&#039;superFunc&#039;&#039;, die alle Pins des Ports D auf &amp;quot;Ausgang&amp;quot; schaltet, eine Funktion &#039;&#039;ultraFunc&#039;&#039;, die die Ausgänge entsprechend des übergebenen Parameters schaltet, eine Funktion &#039;&#039;gigaFunc&#039;&#039;, die den Status von Port A zurückgibt und eine Funktion &#039;&#039;addFunc&#039;&#039;, die zwei Bytes zu einem 16-bit-Wort addiert. Die Zuweisungen im C-Code (PORTx = ...) verhindern, dass der Compiler die Aufrufe wegoptimiert und dienen nur zur Veranschaulichung der Parameterübergaben.&lt;br /&gt;
&lt;br /&gt;
Zuerst der Assembler-Code. Der Dateiname sei useful.S:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
//; Arbeitsregister (ohne &amp;quot;r&amp;quot;) &lt;br /&gt;
workreg  = 16&lt;br /&gt;
workreg2 = 17&lt;br /&gt;
&lt;br /&gt;
//; Konstante:&lt;br /&gt;
ALLOUT = 0xff&lt;br /&gt;
&lt;br /&gt;
//; ** Setze alle Pins von PortD auf Ausgang **&lt;br /&gt;
//; keine Parameter, keine Rückgabe&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg  // beachte: _SFR_IO_ADDR()&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Setze PORTD auf übergebenen Wert **&lt;br /&gt;
//; Parameter in r24 (LSB immer bei &amp;quot;graden&amp;quot; Nummern)&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zustand von PINA zurückgeben **&lt;br /&gt;
//; Rückgabewerte in r24:r25 (LSB:MSB), hier nur LSB genutzt&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//; ** Zwei Bytes addieren und 16-bit-Wort zurückgeben **&lt;br /&gt;
//; Parameter in r24 (Summand1) und r22 (Summand2) -&lt;br /&gt;
//;  Parameter sind Word-&amp;quot;aligned&amp;quot; d.h. LSB immer auf &amp;quot;graden&amp;quot;&lt;br /&gt;
//;  Registernummern. Bei 8-Bit und 16-Bit Paramtern somit &lt;br /&gt;
//;  beginnend bei r24 dann r22 dann r20 etc.&lt;br /&gt;
//; Rückgabewert in r24:r25&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
   push workreg2&lt;br /&gt;
   clr workreg2&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
   pop workreg2&lt;br /&gt;
   pop workreg&lt;br /&gt;
   ret&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
//; oh je - sorry - Mein AVR-Assembler ist eingerostet, hoffe das stimmt so...&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Makefile ist der Name der Assembler-Quellcodedatei einzutragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
ASRC = useful.S&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Aufruf erfolgt dann im C-Code so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
extern void superFunc(void);&lt;br /&gt;
extern void ultraFunc(uint8_t setVal);&lt;br /&gt;
extern uint8_t gigaFunc(void);&lt;br /&gt;
extern uint16_t addFunc(uint8_t w1, uint8_t w2);&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
[...]&lt;br /&gt;
  superFunc();&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
&lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
[...]&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird wieder in der lss-Datei ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
   superFunc();&lt;br /&gt;
 148:	0e 94 f6 00 	call	0x1ec&lt;br /&gt;
  &lt;br /&gt;
  ultraFunc(0x55);&lt;br /&gt;
 14c:	85 e5       	ldi	r24, 0x55	; 85&lt;br /&gt;
 14e:	0e 94 fb 00 	call	0x1f6&lt;br /&gt;
  &lt;br /&gt;
  PORTD = gigaFunc();&lt;br /&gt;
 152:	0e 94 fd 00 	call	0x1fa&lt;br /&gt;
 156:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
  &lt;br /&gt;
  PORTA = (addFunc(0xF0, 0x11) &amp;amp; 0xff);&lt;br /&gt;
 158:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 15a:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 15c:	0e 94 ff 00 	call	0x1fe&lt;br /&gt;
 160:	8b bb       	out	0x1b, r24	; 27&lt;br /&gt;
  PORTB = (addFunc(0xF0, 0x11) &amp;gt;&amp;gt; 8);&lt;br /&gt;
 162:	61 e1       	ldi	r22, 0x11	; 17&lt;br /&gt;
 164:	80 ef       	ldi	r24, 0xF0	; 240&lt;br /&gt;
 166:	0e 94 fc 00 	call	0x1f8&lt;br /&gt;
 16a:	89 2f       	mov	r24, r25&lt;br /&gt;
 16c:	99 27       	eor	r25, r25&lt;br /&gt;
 16e:	88 bb       	out	0x18, r24	; 24&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
000001ec &amp;lt;superFunc&amp;gt;:&lt;br /&gt;
// setze alle Pins von PortD auf Ausgang&lt;br /&gt;
.global superFunc&lt;br /&gt;
.func superFunc&lt;br /&gt;
superFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1ec:	0f 93       	push	r16&lt;br /&gt;
   ldi workreg, ALLOUT&lt;br /&gt;
 1ee:	0f ef       	ldi	r16, 0xFF	; 255&lt;br /&gt;
   out  _SFR_IO_ADDR(DDRD), workreg&lt;br /&gt;
 1f0:	01 bb       	out	0x11, r16	; 17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 1f2:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 1f4:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001f6 &amp;lt;ultraFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// setze PORTD auf übergebenen Wert&lt;br /&gt;
.global ultraFunc&lt;br /&gt;
.func ultraFunc&lt;br /&gt;
ultraFunc:&lt;br /&gt;
   out  _SFR_IO_ADDR(PORTD), 24&lt;br /&gt;
 1f6:	82 bb       	out	0x12, r24	; 18&lt;br /&gt;
   ret&lt;br /&gt;
 1f8:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fa &amp;lt;gigaFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Zustand von PINA zurückgeben&lt;br /&gt;
.global gigaFunc&lt;br /&gt;
.func gigaFunc&lt;br /&gt;
gigaFunc:&lt;br /&gt;
   in 24, _SFR_IO_ADDR(PINA)&lt;br /&gt;
 1fa:	89 b3       	in	r24, 0x19	; 25&lt;br /&gt;
   ret&lt;br /&gt;
 1fc:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
000001fe &amp;lt;addFunc&amp;gt;:&lt;br /&gt;
.endfunc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// zwei Bytes addieren und 16-bit-Wort zurückgeben&lt;br /&gt;
.global addFunc&lt;br /&gt;
.func addFunc&lt;br /&gt;
addFunc:&lt;br /&gt;
   push workreg&lt;br /&gt;
 1fe:	0f 93       	push	r16&lt;br /&gt;
   push workreg2&lt;br /&gt;
 200:	1f 93       	push	r17&lt;br /&gt;
   clr workreg2&lt;br /&gt;
 202:	11 27       	eor	r17, r17&lt;br /&gt;
   mov workreg, 22&lt;br /&gt;
 204:	06 2f       	mov	r16, r22&lt;br /&gt;
   add workreg, 24&lt;br /&gt;
 206:	08 0f       	add	r16, r24&lt;br /&gt;
   adc workreg2, 1    // r1 - assumed to be always zero ...&lt;br /&gt;
 208:	11 1d       	adc	r17, r1&lt;br /&gt;
   movw r24, workreg&lt;br /&gt;
 20a:	c8 01       	movw	r24, r16&lt;br /&gt;
   pop workreg2&lt;br /&gt;
 20c:	1f 91       	pop	r17&lt;br /&gt;
   pop workreg&lt;br /&gt;
 20e:	0f 91       	pop	r16&lt;br /&gt;
   ret&lt;br /&gt;
 210:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zuweisung von Registern zu Parameternummer und die Register für die Rückgabewerte sind in den &amp;quot;Register Usage Guidelines&amp;quot; der avr-libc-Dokumentation erläutert.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/assembler.html avr-libc-Dokumentation: Related Pages/avr-libc and assembler programs]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage avr-libc-Dokumentation: Related Pages/FAQ/&amp;quot;What registers are used by the C compiler?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
== Globale Variablen für Datenaustausch ==&lt;br /&gt;
&lt;br /&gt;
Oftmals kommt man um globale Variablen nicht herum, z.B. um den Datenaustausch zwischen Hauptprogramm und Interrupt-Routinen zu realisieren. &lt;br /&gt;
Hierzu muss man im Assembler wissen, wo genau die Variable vom C-Compiler abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
Hierzu muss die Variable, hier &amp;quot;zaehler&amp;quot; genannt, zuerst im C-Code als Global definiert werden, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
int16_t main (void)&lt;br /&gt;
{&lt;br /&gt;
    // irgendein Code, in dem zaehler benutzt werden kann&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im foldenden Assembler-Beispiel wird der Externe Interrupt0  verwendet, um den Zähler hochzuzählen. Es fehlen die Initialisierungen des Interrupts und die Interrupt-Freigabe, so richtig sinnvoll ist der Code auch nicht, aber er zeigt (hoffentlich) wie es geht.&lt;br /&gt;
&lt;br /&gt;
Im Umgang mit Interrupt-Vektoren gilt beim GCC-Assembler das Gleiche, wie bei C: Man muss die exakte Schreibweise beachten, ansonsten wird nicht der Interrupt-Vektor angelegt, sondern eine neue Funktion - und man wundert sich, dass nichts funktionert (vgl. das AVR-GCC-Handbuch).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
.extern zaehler&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      //; wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     //; Status-Register (SREG) sichern!&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     lds temp,zaehler               //; Wert aus dem Speicher lesen&lt;br /&gt;
     inc temp                       //; bearbeiten&lt;br /&gt;
     sts zaehler,temp               //; und wieder zurückschreiben&lt;br /&gt;
&lt;br /&gt;
     pop temp                       //; die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen im Assemblerfile anlegen ===&lt;br /&gt;
&lt;br /&gt;
Alternativ können Variablen aber auch im Assemblerfile angelegt werden. Dadurch kann auf eine .c-Datei verzichtet werden. Für das obige Beispiel könnte der Quelltext dann die Dateien zaehl_asm.S und zaehl_asm.h abgelegt werden, so dass nur noch zaehl_asm.S mit kompiliert werden müsste.&lt;br /&gt;
&lt;br /&gt;
Anstatt im Assemblerfile über das Schlüsselwort &#039;&#039;.extern &#039;&#039; auf eine vorhandene Variable zu verweisen, wird dazu mit dem Schlüsselwort &#039;&#039;.comm&#039;&#039; die benötigte Anzahl von Bytes für eine Variable reserviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.S&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
//; 1 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 1&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Headerdatei wird dann auf die Variable nur noch verwiesen (Schlüsselwort &#039;&#039;extern&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;zaehl_asm.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef ZAEHL_ASM_H&lt;br /&gt;
#define ZAEHL_ASM_H&lt;br /&gt;
&lt;br /&gt;
extern volatile uint8_t zaehler;&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu globalen Variablen in C werden so angelegte Variablen nicht automatisch mit dem Wert 0 initialisiert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer als 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Variablen, die größer als &#039;&#039;&#039;ein&#039;&#039;&#039; Byte sind, können in Assembler auf ähnliche Art angesprochen werden. Hierzu müssen nur genug Bytes angefordert werden, um die Variable aufzunehmen. Soll z.B. für den Zähler eine Variable vom Typ &#039;&#039;unsigned long&#039;&#039;, also &#039;&#039;uint32_t&#039;&#039; verwendet werden, so müssen 4 Bytes reserviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die dazugehörige Deklaration im Headerfile wäre dann:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
extern volatile uint32_t zaehler;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen, die größer als ein Byte sind, werden die Werte beginnend mit dem niederwertigsten Byte im RAM abgelegt. Das folgende Codeschnippsel zeigt, wie unter Assembler auf die einzelnen Bytes zugegriffen werden kann. Dazu wird im Interrupt nun ein 32-Bit Zähler erhöht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
temp = 16&lt;br /&gt;
&lt;br /&gt;
// 4 Byte im RAM für den Zähler reservieren&lt;br /&gt;
.comm zaehler, 4&lt;br /&gt;
&lt;br /&gt;
.global INT0_vect&lt;br /&gt;
INT0_vect:&lt;br /&gt;
&lt;br /&gt;
     push temp                      // wichtig: Benutzte Register und das&lt;br /&gt;
     in temp,_SFR_IO_ADDR(SREG)     // Status-Register (SREG) sichern !&lt;br /&gt;
     push temp&lt;br /&gt;
&lt;br /&gt;
     // 32-Bit-Zähler incrementieren&lt;br /&gt;
     lds temp, (zaehler + 0)        // 0. Byte (niederwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 0), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
     lds temp, (zaehler + 1)        // 1. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 1), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 2)        // 2. Byte&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 2), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
&lt;br /&gt;
     lds temp, (zaehler + 3)        // 3. Byte (höchstwertigstes Byte)&lt;br /&gt;
     inc temp&lt;br /&gt;
     sts (zaehler + 3), temp&lt;br /&gt;
     brne RAUS&lt;br /&gt;
	&lt;br /&gt;
RAUS:&lt;br /&gt;
     pop temp                       // die benutzten Register wiederherstellen&lt;br /&gt;
     out _SFR_IO_ADDR(SREG),temp&lt;br /&gt;
     pop temp&lt;br /&gt;
     reti&lt;br /&gt;
&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TODO:&#039;&#039;&#039; 16-Bit / 32-Bit Variablen, Zugriff auf Arrays (Strings)&lt;br /&gt;
&lt;br /&gt;
= Anhang =&lt;br /&gt;
&lt;br /&gt;
== Besonderheiten bei der Anpassung bestehenden Quellcodes ==&lt;br /&gt;
&lt;br /&gt;
Einige Funktionen, die in frühren Versionen der avr-libc vorhanden waren, werden inzwischen als veraltet angesehen. Sie sind nicht mehr vorhanden oder als &#039;&#039;deprecated&#039;&#039; (missbilligt) ausgewiesen und Definitionen in &amp;lt;compat/deprecated.h&amp;gt; verschoben. Es empfiehlt sich, vorhandenen Code zu portieren und die alten Funktionen nicht mehr zu nutzen, auch wenn diese noch zur Verfügung stehen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zur Deklaration von Interrupt-Routinen ===&lt;br /&gt;
&lt;br /&gt;
Die Funktionen (eigentlich Makros) &#039;&#039;SIGNAL&#039;&#039; und &#039;&#039;INTERRUPT&#039;&#039; zur Deklaration von Interruptroutinen sollten nicht mehr genutzt werden. &lt;br /&gt;
&lt;br /&gt;
In aktuellen Versionen der avr-libc (z.B. avr-libc 1.4.3 aus WinAVR 20060125) werden Interruptroutinen, die &#039;&#039;&#039;nicht&#039;&#039;&#039; durch andere Interrupts &#039;&#039;&#039;unterbrechbar&#039;&#039;&#039; sind, mit ISR deklariert (siehe Abschnitt im Hauptteil). Auch die Benennung wurden vereinheitlicht und an die üblichen Bezeichnungen in den AVR Datenblättern angepasst. In der Dokumentation der avr-libc sind alte und neue Bezeichnungen in der Tabelle gegenübergestellt. Die erforderlichen Schritte zur Portierung:&lt;br /&gt;
&lt;br /&gt;
* #include von avr/signal.h entfernen&lt;br /&gt;
* SIGNAL duch ISR ersetzen&lt;br /&gt;
* Name des Interrupt-Vektors anpassen (SIG_* durch entsprechendes *_vect)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für die Anpassung zuerst ein &amp;quot;alter&amp;quot; Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer2 Output Compare bei einem ATmega8 */&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE2)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt wird der Vektor mit TIMER2 COMP bezeichnet. Die Bezeichnung in der avr-libc entspricht dem Namen im Datenblatt, Leerzeichen werden durch Unterstriche (_) ersetzt und ein _vect angehängt. &lt;br /&gt;
&lt;br /&gt;
Der neue Code sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt; &lt;br /&gt;
/* signal.h entfällt */&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER2_COMP_vect)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Unklarheiten bezüglich der neuen Vektorlabels hilft (noch) ein Blick in die Headerdatei des entsprechenden Controllers. Für das vorherige Beispiel also der Blick in die Datei iom8.h für den ATmega8, dort findet man die veraltete Bezeichnung unterhalb der aktuellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
/* avr/iom8.h - definitions for ATmega8 */&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect		_VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2		_VECTOR(3)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Nachfolgendes mit avr-libc 1.4.5 (in WinAVR 1/2007 behoben - noch eine Weile auskommentiert lassen und dann löschen.&lt;br /&gt;
Konnte in alten Versionen signal.h ohne interrupt.h eingebunden werden, erhält man bei Verwendung der avr-libc Version 1.4.3 (WinAVR 2/2005) beim Compilieren eine Fehlermeldung, da mit signal.h nicht die erforderlichen Definitionen eingebunden werden. Der Lösungsvorschlag in signal.h auch interrupt.h einzubinden, wurde von den avr-libc-Enwicklern akzeptiert und das Problem ist  im Quellcode (CVS) bereits behoben. Es ist aber noch keine avr-libc-&amp;quot;Release&amp;quot; bzw. noch kein WinAVR mit dieser avr-libc-Korrektur verfügbar (Stand 5.2.2006). Will oder kann man den Quellcode nicht aktualisieren, gibt es folgende Alternativen:&lt;br /&gt;
* in Quellcodedateien, in denen nur avr/signal.h eingebunden wird, interrupt.h einbinden (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;). signal.h weiterhin einbinden, falls Kompatibiltät mit alten Versionen gewünscht ist.&lt;br /&gt;
* in der Datei signal.h (bein WinAVR in c:/WinAVR/avr/include/avr/signal.h) ein (&#039;&#039;#include &amp;amp;lt;avr/interrupt.h&amp;amp;gt;&#039;&#039;) ergänzen --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;unterbrechbare&#039;&#039;&#039; Interruptroutinen, die mittels &#039;&#039;INTERRUPT&#039;&#039; deklariert sind, gibt es keinen direkten Ersatz in Form eines Makros. Solche Routinen sind laut Dokumentation der avr-libc in folgender Form zu deklarieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void XXX_vect(void) __attribute__((interrupt));&lt;br /&gt;
void XXX_vect(void) {&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* ** alt ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
INTERRUPT(SIG_OVERFLOW0)&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ** neu: ** */&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void TIMER0_OVF_vect(void) __attribute__((interrupt));&lt;br /&gt;
void TIMER0_OVF_vect(void) &lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von &#039;&#039;INTERRUPT&#039;&#039; die Header-Datei &#039;&#039;compat/deprecated.h&#039;&#039; einzubinden. Man sollte bei dieser Gelegenheit jedoch nochmals überprüfen, ob die Funktionalität von &#039;&#039;INTERRUPT&#039;&#039; tatsächlich gewollt ist. In vielen Fällen wurde &#039;&#039;INTERRUPT&#039;&#039; dort genutzt, wo eigentlich &#039;&#039;SIGNAL&#039;&#039; (nunmehr &#039;&#039;ISR&#039;&#039;) hätte genutzt werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;inp&#039;&#039; und &#039;&#039;outp&#039;&#039; zum Einlesen bzw. Schreiben von Registern sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
unsigned char i, j;&lt;br /&gt;
&lt;br /&gt;
// alt:&lt;br /&gt;
  i = inp(PINA);&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  outp(PORTB, j);&lt;br /&gt;
&lt;br /&gt;
// neu (nicht mehr wirklich neu...):&lt;br /&gt;
  i = PINA&lt;br /&gt;
  j = 0xff;&lt;br /&gt;
  PORTB = j;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von inp und outp die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete Funktionen zum Zugriff auf Bits in Registern ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;cbi&#039;&#039; und &#039;&#039;sbi&#039;&#039; zum Löschen und Setzen von Bits sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg. Die Bezeichnung ist ohnehin irreführend da die Funktionen nur für Register mit Adressen im unteren Speicherbereich tatsächlich in die Assembleranweisungen cbi und sbi übersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// alt:&lt;br /&gt;
  sbi(PORTB, PB2);&lt;br /&gt;
  cbi(PORTC, PC1);&lt;br /&gt;
&lt;br /&gt;
// neu (auch nicht mehr wirklich neu...):&lt;br /&gt;
  PORTB |=  (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
  PORTC &amp;amp;= ~(1&amp;lt;&amp;lt;PC1);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von sbi und cbi die Header-Datei &#039;&#039;&#039;compat/deprecated.h&#039;&#039;&#039; einzubinden. Wer unbedingt will, kann sich natürlich eigene Makros mit aussagekräftigeren Namen definieren. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &amp;amp;= ~(1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1&amp;lt;&amp;lt;(BITNUM)))&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Selbstdefinierte (nicht-standardisierte) ganzzahlige Datentypen ===&lt;br /&gt;
&lt;br /&gt;
Bei den im Folgenden genannten Typdefinitionen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich, bei der Überarbeitung von altem Code die im Abschnitt &#039;&#039;standardisierten ganzzahligen Datentypen&#039;&#039; beschriebenen Datentypen zu nutzen (stdint.h) und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef unsigned char      BYTE;       // besser: uint8_t  aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned short     WORD;       // besser: uint16_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long      DWORD;      // besser: uint32_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
typedef unsigned long long QWORD;      // besser: uint64_t aus &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; BYTE : Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 255. &lt;br /&gt;
&lt;br /&gt;
; WORD : Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 65535. &lt;br /&gt;
&lt;br /&gt;
; DWORD : Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
; QWORD : Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Funktionen im Makefile ==&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken (Libraries/.a-Dateien) hinzufügen ===&lt;br /&gt;
&lt;br /&gt;
Um Funktionen aus Bibliotheken (&amp;quot;echte&amp;quot; Libraries, *.a-Dateien) zu nutzen, sind dem Linker die Namen der Bibliotheken als Parameter zu übergeben. Dazu ist die Option -l (kleines L) vorgesehen, an die der Name der Library angehängt wird. &lt;br /&gt;
&lt;br /&gt;
Dabei ist zu beachten, dass der Name der Library und der Dateiname der Library nicht identisch sind. Der hinter -l angegebene Name entspricht dem Dateinamen der Library ohne die Zeichenfolge &#039;&#039;lib&#039;&#039; am Anfang des Dateinamens und ohne die Endung &#039;&#039;.a&#039;&#039;. Sollen z.B. Funktionen aus einer Library mit dem Dateinamen &#039;&#039;libefsl.a&#039;&#039; eingebunden (gelinkt) werden, lautet der entsprechende Parameter -lefsl (vergl. auch -lm zum Anbinden von libm.a). &lt;br /&gt;
&lt;br /&gt;
In Makefiles wird traditonell eine make-Variable LDLIBS genutzt, in die &amp;quot;l-Parameter&amp;quot; abgelegt werden. Die WinAVR-makefile-Vorlage enthält diese Variable zwar nicht, dies stellt jedoch keine Einschränkung dar, da alle in der make-Variable LDFLAGS abgelegten Parameter an den Linker weitergereicht werden. &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
# Einbinden von Funktionen aus einer Library efsl (Dateiname libefsl.a)&lt;br /&gt;
LDFLAGS += -lefsl&lt;br /&gt;
# Einbinden von Funktionen aus einer Library xyz (Dateiname libxyz.a)&lt;br /&gt;
LDFLAGS += -lxyz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Liegen die Library-Dateien nicht im Standard Library-Suchpfad, sind die Pfade mittels Parameter &#039;&#039;-L&#039;&#039; ebenfalls anzugeben. (Der vordefinierte Suchpfad kann mittels &#039;&#039;avr-gcc --print-search-dirs&#039;&#039; angezeigt werden.)&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Projekt (&amp;quot;superapp2&amp;quot;), in dem der Quellcode von zwei Libraries (efsl und xyz) und der Quellcode der eigentlichen Anwendung in verschiedenen Verzeichnissen mit der folgenden &amp;quot;Baumstruktur&amp;quot; abgelegt sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
superapp2&lt;br /&gt;
|&lt;br /&gt;
+----- efslsource (darin libefsl.a)&lt;br /&gt;
|&lt;br /&gt;
+----- xyzsource (darin libxyz.a)&lt;br /&gt;
|&lt;br /&gt;
+----- firmware (darin Anwendungs-Quellcode und Makefile)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt, dass im Makefile die Verzeichnis efslsource und xyzsource in den Library-Suchpfad aufzunehmen sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
LDFLAGS += -L../efslsource/ -L../xyzsource/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fuse-Bits ===&lt;br /&gt;
&lt;br /&gt;
Zur Berechnung der Fuse-Bits bietet sich neben dem Studium des Datenblattes auch der [http://palmavr.sourceforge.net/cgi-bin/fc.cgi AVR Fuse Calculator] an. Gewarnt werden muss vor der Benutzung von PonyProg, weil dort durch die negierte Darstellung gern Fehler gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Soll die Programmierung von Fuse- und Lockbits automatisiert werden, kann man dies ebenfalls durch Einträge im Makefile vornehmen, die beim Aufruf von &amp;quot;make program&amp;quot; an die genutzte Programmiersoftware übergeben werden. In der makefile-Vorlage von WinAVR (und mfile) gibt es dafuer jedoch keine &amp;quot;Ausfüllhilfe&amp;quot; (Stand 9/2006). Die folgenden Ausführungen gelten für die Programmiersoftware [[AVRDUDE]] (Standard in der WinAVR-Vorlage), können jedoch sinngemäß auf andere Programmiersoftware übertragen werden, die die Angabe der Fuse- und Lockbits-Einstellungen per Kommandozeilenparameter unterstützt (z.B. stk500.exe). Im einfachsten Fall ergänzt man im Makefile einige Variablen, deren Werte natürlich vom verwendeten Controller und den gewünschten Einstellungen abhängen (vgl. Datenblatt Fuse-/Lockbits):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#---------------- Programming Options (avrdude) ----------------&lt;br /&gt;
&lt;br /&gt;
#...&lt;br /&gt;
#Beispiel! f. ATmega16 - nicht einfach uebernehmen! Zahlenwerte anhand&lt;br /&gt;
#--------- des Datenblatts nachvollziehen und gegebenenfalls aendern.&lt;br /&gt;
#&lt;br /&gt;
AVRDUDE_WRITE_LFUSE = -U lfuse:w:0xff:m&lt;br /&gt;
AVRDUDE_WRITE_HFUSE = -U hfuse:w:0xd8:m&lt;br /&gt;
AVRDUDE_WRITE_LOCK  = -U lock:w:0x2f:m&lt;br /&gt;
#...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit diese Variablen auch genutzt werden, ist der Aufruf von avrdude im Makefile entsprechend zu ergänzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Program the device.  &lt;br /&gt;
program: $(TARGET).hex $(TARGET).eep&lt;br /&gt;
# ohne Fuse-/Lock-Einstellungen (nach WinAVR Vorlage Stand 4/2006)&lt;br /&gt;
#	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
#        $(AVRDUDE_WRITE_EEPROM)&lt;br /&gt;
# mit Fuse-/Lock-Einstellungen&lt;br /&gt;
        $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_LFUSE) \&lt;br /&gt;
        $(AVRDUDE_WRITE_HFUSE) $(AVRDUDE_WRITE_FLASH) \&lt;br /&gt;
        $(AVRDUDE_WRITE_EEPROM) $(AVRDUDE_WRITE_LOCK)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weiter Möglichkeit besteht darin, die Fuse- und Lockbit-Einstellungen vom Preprozessor/Compiler generieren zu lassen. Die Fuse-Bits werden dann bei Verwendung von AVRDUDE in eigene Hex-Files geschrieben. Hierzu kann man z.B. folgendes Konstrukt verwenden:&lt;br /&gt;
&lt;br /&gt;
In eine der C-Sourcen wird eine Variable je Fuse-Byte vom Typ &#039;&#039;unsigned char&#039;&#039; deklariert und in eine extra Section gepackt. Dies kann entweder in einem vorhandenen File passieren oder in ein neues (z.B. fuses.c) geschrieben werden. Das File muss im Makefile aber auf jeden Fall mit kompiliert und gelinkt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// tiny 2313 fuses low byte&lt;br /&gt;
#define CKDIV8  7&lt;br /&gt;
#define CKOUT   6&lt;br /&gt;
#define SUT1    5&lt;br /&gt;
#define SUT0    4&lt;br /&gt;
#define CKSEL3  3&lt;br /&gt;
#define CKSEL2  2&lt;br /&gt;
#define CKSEL1  1&lt;br /&gt;
#define CKSEL0  0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses high byte&lt;br /&gt;
#define DWEN       7&lt;br /&gt;
#define EESAVE     6&lt;br /&gt;
#define SPIEN      5&lt;br /&gt;
#define WDTON      4&lt;br /&gt;
#define BODLEVEL2  3&lt;br /&gt;
#define BODLEVEL1  2&lt;br /&gt;
#define BODLEVEL0  1&lt;br /&gt;
#define RSTDISBL   0&lt;br /&gt;
&lt;br /&gt;
// tiny2313 fuses extended byte&lt;br /&gt;
#define SELFPRGEN  0&lt;br /&gt;
&lt;br /&gt;
#define LFUSE         __attribute__ ((section (&amp;quot;lfuses&amp;quot;)))&lt;br /&gt;
#define HFUSE         __attribute__ ((section (&amp;quot;hfuses&amp;quot;)))&lt;br /&gt;
#define EFUSE         __attribute__ ((section (&amp;quot;efuses&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// select ext crystal 3-8Mhz&lt;br /&gt;
unsigned char lfuse LFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;CKDIV8) | (1&amp;lt;&amp;lt;CKOUT) | (1&amp;lt;&amp;lt;CKSEL3) | (1&amp;lt;&amp;lt;CKSEL2) | &lt;br /&gt;
      (0&amp;lt;&amp;lt;CKSEL1) | (1&amp;lt;&amp;lt;CKSEL0) | (0&amp;lt;&amp;lt;SUT1) | (1&amp;lt;&amp;lt;SUT0) );&lt;br /&gt;
unsigned char hfuse HFUSE =&lt;br /&gt;
    ( (1&amp;lt;&amp;lt;DWEN) | (1&amp;lt;&amp;lt;EESAVE) | (0&amp;lt;&amp;lt;SPIEN) | (1&amp;lt;&amp;lt;WDTON) | &lt;br /&gt;
      (1&amp;lt;&amp;lt;BODLEVEL2) | (1&amp;lt;&amp;lt;BODLEVEL1) | (0&amp;lt;&amp;lt;BODLEVEL0) | (1&amp;lt;&amp;lt;RSTDISBL) );&lt;br /&gt;
unsigned char efuse EFUSE =&lt;br /&gt;
    ((0&amp;lt;&amp;lt;SELFPRGEN));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG: Die Bitpositionen wurden nicht vollständig getestet!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Eine &amp;quot;1&amp;quot; bedeutet hier, dass das Fuse-Bit &#039;&#039;nicht&#039;&#039; programmiert wird - die Funktion also i.A. nicht aktiviert ist. Eine &amp;quot;0&amp;quot; hingegen aktiviert die meisten Funktionen. Dies ist wie im Datenblatt (1 = unprogrammed, 0 = programmed).&lt;br /&gt;
&lt;br /&gt;
Das Makefile muss nun noch um folgende Targets erweitert werden (mit Tabulator einrücken - nicht mit Leerzeichen):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
lfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j lfuses --change-section-address lfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-lfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-lfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U lfuse:w:$(TARGET)-lfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
hfuses: build&lt;br /&gt;
        -$(OBJCOPY) -j hfuses --change-section-address hfuses=0 \&lt;br /&gt;
          -O ihex $(TARGET).elf $(TARGET)-hfuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-hfuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U hfuse:w:$(TARGET)-hfuse.hex; \&lt;br /&gt;
        fi;&lt;br /&gt;
&lt;br /&gt;
efuses: build&lt;br /&gt;
        -$(OBJCOPY) -j efuses --change-section-address efuses=0 \&lt;br /&gt;
         -O ihex $(TARGET).elf $(TARGET)-efuse.hex&lt;br /&gt;
        @if [ -f $(TARGET)-efuse.hex ]; then \&lt;br /&gt;
         $(AVRDUDE) $(AVRDUDE_FLAGS) -U efuse:w:$(TARGET)-efuse.hex;&lt;br /&gt;
        fi;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Target &amp;quot;clean&amp;quot; muss noch um die Zeilen&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
        $(REMOVE) $(TARGET)-lfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-hfuse.hex&lt;br /&gt;
        $(REMOVE) $(TARGET)-efuse.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
erweitert werden, wenn auch die Fuse-Dateien gelöscht werden sollen.&lt;br /&gt;
&lt;br /&gt;
Um nun die Fusebits des angeschlossenen Controllers zu programmieren muss lediglichein &amp;quot;make lfuses&amp;quot;, &amp;quot;make hfuses&amp;quot; oder &amp;quot;make efuses&amp;quot; gestartet werden.&lt;br /&gt;
Bei den Fuse-Bits ist besondere Vorsicht geboten, da diese das Programmieren des Controllers unmöglich machen können. Also erst programmieren, wenn man einen HV-Programmierer hat oder ein paar Reserve-AVRs zur Hand ;-)&lt;br /&gt;
&lt;br /&gt;
Um weiterhin den &amp;quot;normalen&amp;quot; Flash beschreiben zu können, ist es wichtig, für das Target &amp;quot;*.hex&amp;quot; im Makefile nicht nur &amp;quot;-R .eeprom&amp;quot; als Parameter zu übergeben sondern zusätzlich noch &amp;quot;-R lfuses -R efuses -R hfuses&amp;quot;. Sonst bekommt AVRDUDE Probleme diese Sections in den Flash (wo sie ja nicht hingehören) zu schreiben.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[AVR_Fuses#Vergleich_der_Fuses_bei_verschiedenen_Programmen|Vergleich der Fuses bei verschiedenen Programmen]]&lt;br /&gt;
&lt;br /&gt;
== Externe Referenzspannung des internen Analog-Digital-Wandlers ==&lt;br /&gt;
&lt;br /&gt;
Die minimale (externe) Referenzspannung des ADC darf nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. z.B. beim ATMEGA8 darf sie laut Datenblatt (S.245, Tabelle 103, Zeile &amp;quot;VREF&amp;quot;) 2,0V nicht unterschreiten. HINWEIS: diese Information findet sich erst in der letzten Revision (Rev. 2486O-10/04) des Datenblatts.&lt;br /&gt;
&lt;br /&gt;
Meiner &amp;lt;!-- Wer? - es gibt inzwischen x Leute die mehr oder weniger viel in diesem Artikel geschrieben haben --&amp;gt; eigenen Erfahrung nach kann man aber (auf eigene Gefahr und natürlich nicht für Seriengeräte) durchaus noch ein klein wenig weiter heruntergehen, bei dem von mir unter die Lupe genommenen ATMEGA8L (also die Low-Voltage-Variante) funktioniert der ADC bei 5V Betriebsspannung mit bis zu VREF=1,15V hinunter korrekt, ab 1,1V und darunter digitalisiert er jedoch nur noch Blödsinn). Ich würde sicherheitshalber nicht unter 1,5V gehen und bei niedrigeren Betriebsspannungen mag sich die Untergrenze für VREF am Pin AREF ggf. nach oben&#039;&#039;&#039;(!)&#039;&#039;&#039; verschieben.&lt;br /&gt;
&lt;br /&gt;
In der letzten Revision des Datenblatts ist außerdem korrigiert, dass ADC4 und ADC5 sehr wohl 10 Bit Genauigkeit bieten (und nicht bloß 8 Bit, wie in älteren Revisionen irrtümlich angegeben.)&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* stdio.h, malloc() &lt;br /&gt;
* Code-Optimierungen (&amp;quot;tricks&amp;quot;), siehe auch Application Note [http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf AVR035: Efficient C Coding for AVR]&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
* SPI siehe [http://www.uni-koblenz.de/~physik/informatik/MCU/SPI.pdf SPI Bus mit Atmel AVR]&lt;br /&gt;
* I²C / TWI Bus [http://www.roboternetz.de/wissen/index.php/TWI]&lt;br /&gt;
* Bootloader (bez. auf boot.h)&lt;br /&gt;
* CAN-Bus&lt;br /&gt;
* Einsatz von einfachen Betriebssystemen auf dem AVR&lt;br /&gt;
* Übersicht zu den C bzw. GCC-predefined Makros (__DATE__, __TIME__,...)&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
* ADC ; &lt;br /&gt;
* Timer&lt;br /&gt;
* USB ; Steuerung mit USB&lt;br /&gt;
* Multiplexen Siebensegment&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28218</id>
		<title>AVR Bootloader FastBoot von Peter Dannegger</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28218"/>
		<updated>2008-05-26T18:40:15Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Komfortables Stand-Alone Firmware-Update */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Karsten Donat&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Was ist ein Bootloader? ==&lt;br /&gt;
&lt;br /&gt;
Der Bootloader ist selbst ein kleines Programm. Es wird beim Start des Controllers zuerst ausgeführt. Damit das PC-Programm für das Firmware-Update sich melden kann, wartet der Bootloader  eine gewisse Zeit (hier 0,33 Sekunden) auf ein Zeichen über die serielle Schnittstelle (UART  RS232, USB). Kommt dies Zeichen wird die neue Firmware gebrannt. Andernfalls wird das eigentliche Programm des Controllers ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dem eigentlichen Anwendungsprogramm geht natürlich der Platz, den der Bootloader benötigt, verloren. Da Peters Bootloader jedoch in ein 512Bytes (256 Worte!) großes Segment passt, stört das nicht weiter.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird der Programmcode des Microcontrollers mit einem ISP-Dongle in den Flash gebrannt. Aus verschiedenen Gründen kann dies jedoch nicht möglich/ gewünscht sein:&lt;br /&gt;
* Speed: Der ISP-Dongle kann langsam sein (z.B. myAVR mit aktuellem AVRDude)&lt;br /&gt;
* PINs: Man braucht die PINs und will ISP abschalten ( Fuses, aber Vorsicht!, danach kann man nur mit dem Bootloader oder einem STK-500 noch an den Flash)&lt;br /&gt;
* Komfort: Man möchte dem Kunden/Nutzer die Möglichkeit geben, eine neue Firmware selbst einzuspielen. In der Regel hat dieser jedoch keinen ISP-Dongle zur Hand. Eine RS232- oder USB-Schnittstelle ist aber oftmals vorhanden.&lt;br /&gt;
* Sicherheit: Man möchte dem Kunden nicht die Firmware in deassemblierbarer Form geben (über geänderten Bootloader kann die Datei verschlüsselt sein).&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/73196 http://www.mikrocontroller.net/topic/73196] - Forum-Beitrag und Bootloader&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725] - Atmel AVR-Studio (Bootloader compilieren)&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR/Bootloader.pdf http://www.KarstenDonat.de/AVR/Bootloader.pdf] - diese Anleitung als PDF&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR http://www.KarstenDonat.de/AVR] - ATMega IDE 2007&lt;br /&gt;
* [http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion (Fuses beim myAVR ändern)&lt;br /&gt;
* [http://www.kreatives-chaos.com/artikel/fastboot17-frontend-python Python Implementierung des bootloaders]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt] - Protokollbeschreibung&lt;br /&gt;
&lt;br /&gt;
== Bootloader anpassen ==&lt;br /&gt;
&lt;br /&gt;
=== CPU-Frequenz, Wartezeit - FASTLOAD.H ===&lt;br /&gt;
&lt;br /&gt;
Bei XTAL die benutzte Frequenz des Controllers einstellen (jungfräuliche AVRs haben oft intern 1MHz aktiviert!).&lt;br /&gt;
Im Standard Makefile von WinAVR steht die Frequenz unter F_CPU&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	XTAL		= 3686400	; 8MHz, not critical&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die Wartezeit auf das Firmware-Update beim Booten anzupassen:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	BootDelay	= XTAL / 3	; 0.33s&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== UART Port/ Pins – M*.ASM ===&lt;br /&gt;
&lt;br /&gt;
Da die verwendeten Pins für die Schnittstelle frei wählbar sind, müssen diese noch eingestellt werden. Für einige Controller liegen bereits Definitionsdateien bei. Für den ATMega 8 heißt die Datei beispielsweise M8.ASM.&lt;br /&gt;
&lt;br /&gt;
STX_PORT: Hier den Sendeport angeben. Für den Hardware-UART des M8 wäre das Port D. Es muss zwar nicht der Hardware-UART sein, wenn man den aber ohnehin als Schnittstelle nutzt, ist es sinnvoll, diesen zu wählen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ    STX_PORT        = PORTD&lt;br /&gt;
.equ    STX_DDR         = DDRD&lt;br /&gt;
.equ    STX             = PD1&lt;br /&gt;
&lt;br /&gt;
.equ    SRX_PIN         = PIND&lt;br /&gt;
.equ    SRX_PORT        = PORTD&lt;br /&gt;
.equ    SRX             = PD0&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&#039;&#039;Beispiel für Hardware-UART im ATMega 8, 48, 168&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn man einen neuen Controller hinzufügen möchte, muss noch die BufferSize angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Compilieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Da der Bootloader in Assembler geschrieben ist, kann WinAVR nicht so ohne weiteres damit umgehen.&lt;br /&gt;
Das einfachste ist, sich das AVRStudio von Atmel herunterzuladen [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725]&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis AvrAssembler2 befindet sich der benötigte Assembler. Das Einfachste ist, sich die unter Appnotes benötigte Include Datei des jeweiligen Controllers (oder das komplette Verzeichnis) und die avrasm2.exe ins Verzeichnis des Bootloaders zu kopieren.&lt;br /&gt;
&lt;br /&gt;
Danach wird der Assembler aufgerufen (m8.asm für ATMega 8) und der Bootloader kompiliert:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
avrasm2 -fI m8.asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das AVRStudio lässt sich unter Linux auch mit WINE installieren (die IDE des AVR Studio 4 geht allerdings nicht).&lt;br /&gt;
Der Aufruf des Assemblers lautet dann:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wine avrasm2.exe -fI M8.ASM&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Brennen des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Der nun erzeugte Bootloader wird mit dem vorhanden ISP Dongle in den AVR gebrannt (Intel Hex Format):&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;avrdude&amp;quot; -p m8 -c avr910 -P com1 -U flash:w:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i&lt;br /&gt;
  -U flash:v:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i -y&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brennen mit AVRDude auf COM 1 und einem AVR910 kompatiblen Dongle (z.B. myAVR USB)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Einstellen der Fuses ==&lt;br /&gt;
&lt;br /&gt;
Damit der Bootloader zu Beginn gestartet wird, müssen die entsprechenden Fuses gesetzt werden. Möglich ist dies u.a. mit der AVRDude GUI.&lt;br /&gt;
* BOOTRST muss auf 0 gestellt werden (aktiviert den Bootloader)&lt;br /&gt;
* BOOTSZ muss auf 10 gestellt werden für 256 Worte&lt;br /&gt;
&lt;br /&gt;
Bei einigen Controllern wie z.B. dem ATMega 48 wird das Flag&lt;br /&gt;
* Selfprogramming enabled gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Fuses mit myAVR ändern ===&lt;br /&gt;
&lt;br /&gt;
Der myAVR Dongle unterstützt leider keine Fuses mit AVRDude. Abhilfe liefert hier jedoch das myAVR Worpad Plus. Die Demo-Version (reicht hierfür) kann unter&lt;br /&gt;
&lt;br /&gt;
[http://www.myavr.info/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.info/download/myAVR_WorkpadPLUS_Demo.exe]&lt;br /&gt;
&lt;br /&gt;
heruntergeladen werden. Nach dem Installieren und Starten im Menü Extras – Fuse- und Lock-Bits wählen. Das Programm ermittelt automatisch den aktuellen Status. Je nach Controller steht der Bootloader Support unter High oder Extended Fuses.&lt;br /&gt;
&lt;br /&gt;
== Brennen des eigentlichen Programmes ==&lt;br /&gt;
&lt;br /&gt;
Damit das Programm mit Peters Tool in den Controller geladen werden kann, muss es im Intel HEX Format vorliegen. Im makefile Template von WinAVR (und auch der ATMegaIDE) ist dies standardmäßig eingestellt.&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Output format. (can be srec, ihex, binary)&lt;br /&gt;
FORMAT = ihex&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach wird Peters Firmware-Update Tool aufgerufen (hier COM2)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fboot /C2 /Pmain.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;hier wird die main.hex als Hauptprogramm gebrannt&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wichtig hierbei ist, dass der Dateiname der alten DOS Konvention entspricht. Also keine langen Dateinamen.&lt;br /&gt;
&lt;br /&gt;
Wenn noch keine Firmware im Controller ist (direkt nach dem Installieren des Bootloaders), startet der Brennvorgang automatisch. Andernfalls wartet das Programm auf den Reset des Controllers. Peters Tool arbeitet nur mit COM1 bis COM4 zusammen. Also ggf. den USB Adapter im Gerätemanager umstellen.&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe sieht dann wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C:\DOCUME~1\zzzzzz\Desktop\avr\_soft\fboot18&amp;gt;fboot18 /Ptest.hex /b9600 /c1&lt;br /&gt;
COM 1 at 9600 Baud: Connected (One wire)&lt;br /&gt;
Bootloader V1.8&lt;br /&gt;
Target: 1E9108&lt;br /&gt;
Buffer: 32 Byte&lt;br /&gt;
Size available: 1534 Byte&lt;br /&gt;
Program test.hex: 00000 - 00073 successful&lt;br /&gt;
CRC: o.k.&lt;br /&gt;
Elapsed time: 1.43 seconds&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bootloader-Support in ATMegaIDE 2007 ==&lt;br /&gt;
&lt;br /&gt;
Die IDE kann wahlweise mit AVRDude und einem entsprechenden ISP-Dongle oder mit dem Bootloader das Programm in den Flash brennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anpassen des Programms ===&lt;br /&gt;
Um das Firmware Update komfortabler zu gestalten, kann ein Software-Reset eingebaut werden, der per RS232- bzw. USB-Schnittstelle oder Tastendruck ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==== Tastendruck ====&lt;br /&gt;
In der mitgelieferten Standard.h gibt es den Befehl void Reset();. Er löst mit Hilfe des Watchdog-Timers einen Hardwarereset aus. Man kann jetzt z.B. auf einen bestimmten Tastendruck (-kombination) hin diesen Reset ausführen.&lt;br /&gt;
&lt;br /&gt;
==== RS232/USB ====&lt;br /&gt;
Alternativ kann ein entsprechender Befehl über die RS232 gesendet werden. Standardmäßig ist dies 0xFF ‚R‘.&lt;br /&gt;
&lt;br /&gt;
=== Einstellungen in der IDE ===&lt;br /&gt;
&lt;br /&gt;
Der Bootloader Support kann bei den Projekt-Eigenschaften eingestellt werden. Beim Brennen wird er dann automatisch benutzt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== ToDo ==&lt;br /&gt;
&lt;br /&gt;
=== Support großer ATMegas ===&lt;br /&gt;
&lt;br /&gt;
Peter und andere arbeiten momentan noch an der Anpassung für größere Controller wie den ATMega 128.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komfortables Stand-Alone Firmware-Update ===&lt;br /&gt;
&lt;br /&gt;
Basierend auf dem Code der IDE wird es im September noch ein Standalone Programm für das Firmware-Update geben. Es wird auch ein entsprechendes Protokoll zur Versionskontrolle haben (die bisherige Soft- und viel wichtiger Hardwareversion wird überprüft). Eine Verschlüsselung der Firmware-Datei ist in Planung.&lt;br /&gt;
&lt;br /&gt;
=== Automatisches erzeugen des Booloaders ===&lt;br /&gt;
Sobald ich Zeit habe kommt in die IDE auch die Möglichkeit, die Anpassungen des Bootloaders von der IDE durchführen zu lassen.&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28217</id>
		<title>AVR Bootloader FastBoot von Peter Dannegger</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28217"/>
		<updated>2008-05-26T18:39:09Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Anpassen des Programms */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Karsten Donat&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Was ist ein Bootloader? ==&lt;br /&gt;
&lt;br /&gt;
Der Bootloader ist selbst ein kleines Programm. Es wird beim Start des Controllers zuerst ausgeführt. Damit das PC-Programm für das Firmware-Update sich melden kann, wartet der Bootloader  eine gewisse Zeit (hier 0,33 Sekunden) auf ein Zeichen über die serielle Schnittstelle (UART  RS232, USB). Kommt dies Zeichen wird die neue Firmware gebrannt. Andernfalls wird das eigentliche Programm des Controllers ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dem eigentlichen Anwendungsprogramm geht natürlich der Platz, den der Bootloader benötigt, verloren. Da Peters Bootloader jedoch in ein 512Bytes (256 Worte!) großes Segment passt, stört das nicht weiter.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird der Programmcode des Microcontrollers mit einem ISP-Dongle in den Flash gebrannt. Aus verschiedenen Gründen kann dies jedoch nicht möglich/ gewünscht sein:&lt;br /&gt;
* Speed: Der ISP-Dongle kann langsam sein (z.B. myAVR mit aktuellem AVRDude)&lt;br /&gt;
* PINs: Man braucht die PINs und will ISP abschalten ( Fuses, aber Vorsicht!, danach kann man nur mit dem Bootloader oder einem STK-500 noch an den Flash)&lt;br /&gt;
* Komfort: Man möchte dem Kunden/Nutzer die Möglichkeit geben, eine neue Firmware selbst einzuspielen. In der Regel hat dieser jedoch keinen ISP-Dongle zur Hand. Eine RS232- oder USB-Schnittstelle ist aber oftmals vorhanden.&lt;br /&gt;
* Sicherheit: Man möchte dem Kunden nicht die Firmware in deassemblierbarer Form geben (über geänderten Bootloader kann die Datei verschlüsselt sein).&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/73196 http://www.mikrocontroller.net/topic/73196] - Forum-Beitrag und Bootloader&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725] - Atmel AVR-Studio (Bootloader compilieren)&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR/Bootloader.pdf http://www.KarstenDonat.de/AVR/Bootloader.pdf] - diese Anleitung als PDF&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR http://www.KarstenDonat.de/AVR] - ATMega IDE 2007&lt;br /&gt;
* [http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion (Fuses beim myAVR ändern)&lt;br /&gt;
* [http://www.kreatives-chaos.com/artikel/fastboot17-frontend-python Python Implementierung des bootloaders]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt] - Protokollbeschreibung&lt;br /&gt;
&lt;br /&gt;
== Bootloader anpassen ==&lt;br /&gt;
&lt;br /&gt;
=== CPU-Frequenz, Wartezeit - FASTLOAD.H ===&lt;br /&gt;
&lt;br /&gt;
Bei XTAL die benutzte Frequenz des Controllers einstellen (jungfräuliche AVRs haben oft intern 1MHz aktiviert!).&lt;br /&gt;
Im Standard Makefile von WinAVR steht die Frequenz unter F_CPU&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	XTAL		= 3686400	; 8MHz, not critical&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die Wartezeit auf das Firmware-Update beim Booten anzupassen:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	BootDelay	= XTAL / 3	; 0.33s&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== UART Port/ Pins – M*.ASM ===&lt;br /&gt;
&lt;br /&gt;
Da die verwendeten Pins für die Schnittstelle frei wählbar sind, müssen diese noch eingestellt werden. Für einige Controller liegen bereits Definitionsdateien bei. Für den ATMega 8 heißt die Datei beispielsweise M8.ASM.&lt;br /&gt;
&lt;br /&gt;
STX_PORT: Hier den Sendeport angeben. Für den Hardware-UART des M8 wäre das Port D. Es muss zwar nicht der Hardware-UART sein, wenn man den aber ohnehin als Schnittstelle nutzt, ist es sinnvoll, diesen zu wählen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ    STX_PORT        = PORTD&lt;br /&gt;
.equ    STX_DDR         = DDRD&lt;br /&gt;
.equ    STX             = PD1&lt;br /&gt;
&lt;br /&gt;
.equ    SRX_PIN         = PIND&lt;br /&gt;
.equ    SRX_PORT        = PORTD&lt;br /&gt;
.equ    SRX             = PD0&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&#039;&#039;Beispiel für Hardware-UART im ATMega 8, 48, 168&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn man einen neuen Controller hinzufügen möchte, muss noch die BufferSize angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Compilieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Da der Bootloader in Assembler geschrieben ist, kann WinAVR nicht so ohne weiteres damit umgehen.&lt;br /&gt;
Das einfachste ist, sich das AVRStudio von Atmel herunterzuladen [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725]&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis AvrAssembler2 befindet sich der benötigte Assembler. Das Einfachste ist, sich die unter Appnotes benötigte Include Datei des jeweiligen Controllers (oder das komplette Verzeichnis) und die avrasm2.exe ins Verzeichnis des Bootloaders zu kopieren.&lt;br /&gt;
&lt;br /&gt;
Danach wird der Assembler aufgerufen (m8.asm für ATMega 8) und der Bootloader kompiliert:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
avrasm2 -fI m8.asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das AVRStudio lässt sich unter Linux auch mit WINE installieren (die IDE des AVR Studio 4 geht allerdings nicht).&lt;br /&gt;
Der Aufruf des Assemblers lautet dann:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wine avrasm2.exe -fI M8.ASM&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Brennen des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Der nun erzeugte Bootloader wird mit dem vorhanden ISP Dongle in den AVR gebrannt (Intel Hex Format):&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;avrdude&amp;quot; -p m8 -c avr910 -P com1 -U flash:w:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i&lt;br /&gt;
  -U flash:v:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i -y&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brennen mit AVRDude auf COM 1 und einem AVR910 kompatiblen Dongle (z.B. myAVR USB)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Einstellen der Fuses ==&lt;br /&gt;
&lt;br /&gt;
Damit der Bootloader zu Beginn gestartet wird, müssen die entsprechenden Fuses gesetzt werden. Möglich ist dies u.a. mit der AVRDude GUI.&lt;br /&gt;
* BOOTRST muss auf 0 gestellt werden (aktiviert den Bootloader)&lt;br /&gt;
* BOOTSZ muss auf 10 gestellt werden für 256 Worte&lt;br /&gt;
&lt;br /&gt;
Bei einigen Controllern wie z.B. dem ATMega 48 wird das Flag&lt;br /&gt;
* Selfprogramming enabled gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Fuses mit myAVR ändern ===&lt;br /&gt;
&lt;br /&gt;
Der myAVR Dongle unterstützt leider keine Fuses mit AVRDude. Abhilfe liefert hier jedoch das myAVR Worpad Plus. Die Demo-Version (reicht hierfür) kann unter&lt;br /&gt;
&lt;br /&gt;
[http://www.myavr.info/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.info/download/myAVR_WorkpadPLUS_Demo.exe]&lt;br /&gt;
&lt;br /&gt;
heruntergeladen werden. Nach dem Installieren und Starten im Menü Extras – Fuse- und Lock-Bits wählen. Das Programm ermittelt automatisch den aktuellen Status. Je nach Controller steht der Bootloader Support unter High oder Extended Fuses.&lt;br /&gt;
&lt;br /&gt;
== Brennen des eigentlichen Programmes ==&lt;br /&gt;
&lt;br /&gt;
Damit das Programm mit Peters Tool in den Controller geladen werden kann, muss es im Intel HEX Format vorliegen. Im makefile Template von WinAVR (und auch der ATMegaIDE) ist dies standardmäßig eingestellt.&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Output format. (can be srec, ihex, binary)&lt;br /&gt;
FORMAT = ihex&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach wird Peters Firmware-Update Tool aufgerufen (hier COM2)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fboot /C2 /Pmain.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;hier wird die main.hex als Hauptprogramm gebrannt&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wichtig hierbei ist, dass der Dateiname der alten DOS Konvention entspricht. Also keine langen Dateinamen.&lt;br /&gt;
&lt;br /&gt;
Wenn noch keine Firmware im Controller ist (direkt nach dem Installieren des Bootloaders), startet der Brennvorgang automatisch. Andernfalls wartet das Programm auf den Reset des Controllers. Peters Tool arbeitet nur mit COM1 bis COM4 zusammen. Also ggf. den USB Adapter im Gerätemanager umstellen.&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe sieht dann wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C:\DOCUME~1\zzzzzz\Desktop\avr\_soft\fboot18&amp;gt;fboot18 /Ptest.hex /b9600 /c1&lt;br /&gt;
COM 1 at 9600 Baud: Connected (One wire)&lt;br /&gt;
Bootloader V1.8&lt;br /&gt;
Target: 1E9108&lt;br /&gt;
Buffer: 32 Byte&lt;br /&gt;
Size available: 1534 Byte&lt;br /&gt;
Program test.hex: 00000 - 00073 successful&lt;br /&gt;
CRC: o.k.&lt;br /&gt;
Elapsed time: 1.43 seconds&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bootloader-Support in ATMegaIDE 2007 ==&lt;br /&gt;
&lt;br /&gt;
Die IDE kann wahlweise mit AVRDude und einem entsprechenden ISP-Dongle oder mit dem Bootloader das Programm in den Flash brennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anpassen des Programms ===&lt;br /&gt;
Um das Firmware Update komfortabler zu gestalten, kann ein Software-Reset eingebaut werden, der per RS232- bzw. USB-Schnittstelle oder Tastendruck ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==== Tastendruck ====&lt;br /&gt;
In der mitgelieferten Standard.h gibt es den Befehl void Reset();. Er löst mit Hilfe des Watchdog-Timers einen Hardwarereset aus. Man kann jetzt z.B. auf einen bestimmten Tastendruck (-kombination) hin diesen Reset ausführen.&lt;br /&gt;
&lt;br /&gt;
==== RS232/USB ====&lt;br /&gt;
Alternativ kann ein entsprechender Befehl über die RS232 gesendet werden. Standardmäßig ist dies 0xFF ‚R‘.&lt;br /&gt;
&lt;br /&gt;
=== Einstellungen in der IDE ===&lt;br /&gt;
&lt;br /&gt;
Der Bootloader Support kann bei den Projekt-Eigenschaften eingestellt werden. Beim Brennen wird er dann automatisch benutzt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== ToDo ==&lt;br /&gt;
&lt;br /&gt;
=== Support großer ATMegas ===&lt;br /&gt;
&lt;br /&gt;
Peter und andere arbeiten momentan noch an der Anpassung für größere Controller wie den ATMega 128.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komfortables Stand-Alone Firmware-Update ===&lt;br /&gt;
&lt;br /&gt;
Basierend auf dem Code der IDE wird es im September noch ein Standalone Programm für das Firmware-Update geben. Es wird auch ein entsprechendes Protokoll zur Versionskontrollle haben. (bisherige Soft- und viel wichtiger Hardwareversion wird gechecked). Eine Verschlüsselung der Firmware-Datei ist in Planung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Automatisches erzeugen des Booloaders ===&lt;br /&gt;
Sobald ich Zeit habe kommt in die IDE auch die Möglichkeit, die Anpassungen des Bootloaders von der IDE durchführen zu lassen.&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28216</id>
		<title>AVR Bootloader FastBoot von Peter Dannegger</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28216"/>
		<updated>2008-05-26T18:37:45Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Brennen des eigentlichen Programmes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Karsten Donat&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Was ist ein Bootloader? ==&lt;br /&gt;
&lt;br /&gt;
Der Bootloader ist selbst ein kleines Programm. Es wird beim Start des Controllers zuerst ausgeführt. Damit das PC-Programm für das Firmware-Update sich melden kann, wartet der Bootloader  eine gewisse Zeit (hier 0,33 Sekunden) auf ein Zeichen über die serielle Schnittstelle (UART  RS232, USB). Kommt dies Zeichen wird die neue Firmware gebrannt. Andernfalls wird das eigentliche Programm des Controllers ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dem eigentlichen Anwendungsprogramm geht natürlich der Platz, den der Bootloader benötigt, verloren. Da Peters Bootloader jedoch in ein 512Bytes (256 Worte!) großes Segment passt, stört das nicht weiter.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird der Programmcode des Microcontrollers mit einem ISP-Dongle in den Flash gebrannt. Aus verschiedenen Gründen kann dies jedoch nicht möglich/ gewünscht sein:&lt;br /&gt;
* Speed: Der ISP-Dongle kann langsam sein (z.B. myAVR mit aktuellem AVRDude)&lt;br /&gt;
* PINs: Man braucht die PINs und will ISP abschalten ( Fuses, aber Vorsicht!, danach kann man nur mit dem Bootloader oder einem STK-500 noch an den Flash)&lt;br /&gt;
* Komfort: Man möchte dem Kunden/Nutzer die Möglichkeit geben, eine neue Firmware selbst einzuspielen. In der Regel hat dieser jedoch keinen ISP-Dongle zur Hand. Eine RS232- oder USB-Schnittstelle ist aber oftmals vorhanden.&lt;br /&gt;
* Sicherheit: Man möchte dem Kunden nicht die Firmware in deassemblierbarer Form geben (über geänderten Bootloader kann die Datei verschlüsselt sein).&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/73196 http://www.mikrocontroller.net/topic/73196] - Forum-Beitrag und Bootloader&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725] - Atmel AVR-Studio (Bootloader compilieren)&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR/Bootloader.pdf http://www.KarstenDonat.de/AVR/Bootloader.pdf] - diese Anleitung als PDF&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR http://www.KarstenDonat.de/AVR] - ATMega IDE 2007&lt;br /&gt;
* [http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion (Fuses beim myAVR ändern)&lt;br /&gt;
* [http://www.kreatives-chaos.com/artikel/fastboot17-frontend-python Python Implementierung des bootloaders]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt] - Protokollbeschreibung&lt;br /&gt;
&lt;br /&gt;
== Bootloader anpassen ==&lt;br /&gt;
&lt;br /&gt;
=== CPU-Frequenz, Wartezeit - FASTLOAD.H ===&lt;br /&gt;
&lt;br /&gt;
Bei XTAL die benutzte Frequenz des Controllers einstellen (jungfräuliche AVRs haben oft intern 1MHz aktiviert!).&lt;br /&gt;
Im Standard Makefile von WinAVR steht die Frequenz unter F_CPU&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	XTAL		= 3686400	; 8MHz, not critical&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die Wartezeit auf das Firmware-Update beim Booten anzupassen:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	BootDelay	= XTAL / 3	; 0.33s&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== UART Port/ Pins – M*.ASM ===&lt;br /&gt;
&lt;br /&gt;
Da die verwendeten Pins für die Schnittstelle frei wählbar sind, müssen diese noch eingestellt werden. Für einige Controller liegen bereits Definitionsdateien bei. Für den ATMega 8 heißt die Datei beispielsweise M8.ASM.&lt;br /&gt;
&lt;br /&gt;
STX_PORT: Hier den Sendeport angeben. Für den Hardware-UART des M8 wäre das Port D. Es muss zwar nicht der Hardware-UART sein, wenn man den aber ohnehin als Schnittstelle nutzt, ist es sinnvoll, diesen zu wählen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ    STX_PORT        = PORTD&lt;br /&gt;
.equ    STX_DDR         = DDRD&lt;br /&gt;
.equ    STX             = PD1&lt;br /&gt;
&lt;br /&gt;
.equ    SRX_PIN         = PIND&lt;br /&gt;
.equ    SRX_PORT        = PORTD&lt;br /&gt;
.equ    SRX             = PD0&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&#039;&#039;Beispiel für Hardware-UART im ATMega 8, 48, 168&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn man einen neuen Controller hinzufügen möchte, muss noch die BufferSize angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Compilieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Da der Bootloader in Assembler geschrieben ist, kann WinAVR nicht so ohne weiteres damit umgehen.&lt;br /&gt;
Das einfachste ist, sich das AVRStudio von Atmel herunterzuladen [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725]&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis AvrAssembler2 befindet sich der benötigte Assembler. Das Einfachste ist, sich die unter Appnotes benötigte Include Datei des jeweiligen Controllers (oder das komplette Verzeichnis) und die avrasm2.exe ins Verzeichnis des Bootloaders zu kopieren.&lt;br /&gt;
&lt;br /&gt;
Danach wird der Assembler aufgerufen (m8.asm für ATMega 8) und der Bootloader kompiliert:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
avrasm2 -fI m8.asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das AVRStudio lässt sich unter Linux auch mit WINE installieren (die IDE des AVR Studio 4 geht allerdings nicht).&lt;br /&gt;
Der Aufruf des Assemblers lautet dann:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wine avrasm2.exe -fI M8.ASM&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Brennen des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Der nun erzeugte Bootloader wird mit dem vorhanden ISP Dongle in den AVR gebrannt (Intel Hex Format):&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;avrdude&amp;quot; -p m8 -c avr910 -P com1 -U flash:w:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i&lt;br /&gt;
  -U flash:v:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i -y&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brennen mit AVRDude auf COM 1 und einem AVR910 kompatiblen Dongle (z.B. myAVR USB)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Einstellen der Fuses ==&lt;br /&gt;
&lt;br /&gt;
Damit der Bootloader zu Beginn gestartet wird, müssen die entsprechenden Fuses gesetzt werden. Möglich ist dies u.a. mit der AVRDude GUI.&lt;br /&gt;
* BOOTRST muss auf 0 gestellt werden (aktiviert den Bootloader)&lt;br /&gt;
* BOOTSZ muss auf 10 gestellt werden für 256 Worte&lt;br /&gt;
&lt;br /&gt;
Bei einigen Controllern wie z.B. dem ATMega 48 wird das Flag&lt;br /&gt;
* Selfprogramming enabled gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Fuses mit myAVR ändern ===&lt;br /&gt;
&lt;br /&gt;
Der myAVR Dongle unterstützt leider keine Fuses mit AVRDude. Abhilfe liefert hier jedoch das myAVR Worpad Plus. Die Demo-Version (reicht hierfür) kann unter&lt;br /&gt;
&lt;br /&gt;
[http://www.myavr.info/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.info/download/myAVR_WorkpadPLUS_Demo.exe]&lt;br /&gt;
&lt;br /&gt;
heruntergeladen werden. Nach dem Installieren und Starten im Menü Extras – Fuse- und Lock-Bits wählen. Das Programm ermittelt automatisch den aktuellen Status. Je nach Controller steht der Bootloader Support unter High oder Extended Fuses.&lt;br /&gt;
&lt;br /&gt;
== Brennen des eigentlichen Programmes ==&lt;br /&gt;
&lt;br /&gt;
Damit das Programm mit Peters Tool in den Controller geladen werden kann, muss es im Intel HEX Format vorliegen. Im makefile Template von WinAVR (und auch der ATMegaIDE) ist dies standardmäßig eingestellt.&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Output format. (can be srec, ihex, binary)&lt;br /&gt;
FORMAT = ihex&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach wird Peters Firmware-Update Tool aufgerufen (hier COM2)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fboot /C2 /Pmain.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;hier wird die main.hex als Hauptprogramm gebrannt&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wichtig hierbei ist, dass der Dateiname der alten DOS Konvention entspricht. Also keine langen Dateinamen.&lt;br /&gt;
&lt;br /&gt;
Wenn noch keine Firmware im Controller ist (direkt nach dem Installieren des Bootloaders), startet der Brennvorgang automatisch. Andernfalls wartet das Programm auf den Reset des Controllers. Peters Tool arbeitet nur mit COM1 bis COM4 zusammen. Also ggf. den USB Adapter im Gerätemanager umstellen.&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe sieht dann wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C:\DOCUME~1\zzzzzz\Desktop\avr\_soft\fboot18&amp;gt;fboot18 /Ptest.hex /b9600 /c1&lt;br /&gt;
COM 1 at 9600 Baud: Connected (One wire)&lt;br /&gt;
Bootloader V1.8&lt;br /&gt;
Target: 1E9108&lt;br /&gt;
Buffer: 32 Byte&lt;br /&gt;
Size available: 1534 Byte&lt;br /&gt;
Program test.hex: 00000 - 00073 successful&lt;br /&gt;
CRC: o.k.&lt;br /&gt;
Elapsed time: 1.43 seconds&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bootloader-Support in ATMegaIDE 2007 ==&lt;br /&gt;
&lt;br /&gt;
Die IDE kann wahlweise mit AVRDude und einem entsprechenden ISP-Dongle oder mit dem Bootloader das Programm in den Flash brennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anpassen des Programms ===&lt;br /&gt;
Um das Firmware Update komfortabler zu gestalten kann ein Software-Reset eingebaut werden, der per RS232/ USB Schnittstelle oder Tastendruck ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==== Tastendruck ====&lt;br /&gt;
In der mitgelieferten Standard.h gibt es den Befehl void Reset();. Er löst mit Hilfe des Watchdog-Timers einen Hardwarereset aus.&lt;br /&gt;
Man kann jetzt z.B. auf einen bestimmten Tastendruck (-kombination) hin diesen Reset ausführen.&lt;br /&gt;
&lt;br /&gt;
==== RS232/ USB ====&lt;br /&gt;
Alternativ kann ein entsprechender Befehl über die RS232 gesendet werden. Standardmäßig ist dies 0xFF ‚R‘.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Einstellungen in der IDE ===&lt;br /&gt;
&lt;br /&gt;
Der Bootloader Support kann bei den Projekt-Eigenschaften eingestellt werden. Beim Brennen wird er dann automatisch benutzt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== ToDo ==&lt;br /&gt;
&lt;br /&gt;
=== Support großer ATMegas ===&lt;br /&gt;
&lt;br /&gt;
Peter und andere arbeiten momentan noch an der Anpassung für größere Controller wie den ATMega 128.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komfortables Stand-Alone Firmware-Update ===&lt;br /&gt;
&lt;br /&gt;
Basierend auf dem Code der IDE wird es im September noch ein Standalone Programm für das Firmware-Update geben. Es wird auch ein entsprechendes Protokoll zur Versionskontrollle haben. (bisherige Soft- und viel wichtiger Hardwareversion wird gechecked). Eine Verschlüsselung der Firmware-Datei ist in Planung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Automatisches erzeugen des Booloaders ===&lt;br /&gt;
Sobald ich Zeit habe kommt in die IDE auch die Möglichkeit, die Anpassungen des Bootloaders von der IDE durchführen zu lassen.&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28215</id>
		<title>AVR Bootloader FastBoot von Peter Dannegger</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28215"/>
		<updated>2008-05-26T18:34:53Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Fuses mit myAVR ändern */ Link korrigiert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Karsten Donat&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Was ist ein Bootloader? ==&lt;br /&gt;
&lt;br /&gt;
Der Bootloader ist selbst ein kleines Programm. Es wird beim Start des Controllers zuerst ausgeführt. Damit das PC-Programm für das Firmware-Update sich melden kann, wartet der Bootloader  eine gewisse Zeit (hier 0,33 Sekunden) auf ein Zeichen über die serielle Schnittstelle (UART  RS232, USB). Kommt dies Zeichen wird die neue Firmware gebrannt. Andernfalls wird das eigentliche Programm des Controllers ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dem eigentlichen Anwendungsprogramm geht natürlich der Platz, den der Bootloader benötigt, verloren. Da Peters Bootloader jedoch in ein 512Bytes (256 Worte!) großes Segment passt, stört das nicht weiter.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird der Programmcode des Microcontrollers mit einem ISP-Dongle in den Flash gebrannt. Aus verschiedenen Gründen kann dies jedoch nicht möglich/ gewünscht sein:&lt;br /&gt;
* Speed: Der ISP-Dongle kann langsam sein (z.B. myAVR mit aktuellem AVRDude)&lt;br /&gt;
* PINs: Man braucht die PINs und will ISP abschalten ( Fuses, aber Vorsicht!, danach kann man nur mit dem Bootloader oder einem STK-500 noch an den Flash)&lt;br /&gt;
* Komfort: Man möchte dem Kunden/Nutzer die Möglichkeit geben, eine neue Firmware selbst einzuspielen. In der Regel hat dieser jedoch keinen ISP-Dongle zur Hand. Eine RS232- oder USB-Schnittstelle ist aber oftmals vorhanden.&lt;br /&gt;
* Sicherheit: Man möchte dem Kunden nicht die Firmware in deassemblierbarer Form geben (über geänderten Bootloader kann die Datei verschlüsselt sein).&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/73196 http://www.mikrocontroller.net/topic/73196] - Forum-Beitrag und Bootloader&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725] - Atmel AVR-Studio (Bootloader compilieren)&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR/Bootloader.pdf http://www.KarstenDonat.de/AVR/Bootloader.pdf] - diese Anleitung als PDF&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR http://www.KarstenDonat.de/AVR] - ATMega IDE 2007&lt;br /&gt;
* [http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion (Fuses beim myAVR ändern)&lt;br /&gt;
* [http://www.kreatives-chaos.com/artikel/fastboot17-frontend-python Python Implementierung des bootloaders]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt] - Protokollbeschreibung&lt;br /&gt;
&lt;br /&gt;
== Bootloader anpassen ==&lt;br /&gt;
&lt;br /&gt;
=== CPU-Frequenz, Wartezeit - FASTLOAD.H ===&lt;br /&gt;
&lt;br /&gt;
Bei XTAL die benutzte Frequenz des Controllers einstellen (jungfräuliche AVRs haben oft intern 1MHz aktiviert!).&lt;br /&gt;
Im Standard Makefile von WinAVR steht die Frequenz unter F_CPU&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	XTAL		= 3686400	; 8MHz, not critical&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die Wartezeit auf das Firmware-Update beim Booten anzupassen:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	BootDelay	= XTAL / 3	; 0.33s&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== UART Port/ Pins – M*.ASM ===&lt;br /&gt;
&lt;br /&gt;
Da die verwendeten Pins für die Schnittstelle frei wählbar sind, müssen diese noch eingestellt werden. Für einige Controller liegen bereits Definitionsdateien bei. Für den ATMega 8 heißt die Datei beispielsweise M8.ASM.&lt;br /&gt;
&lt;br /&gt;
STX_PORT: Hier den Sendeport angeben. Für den Hardware-UART des M8 wäre das Port D. Es muss zwar nicht der Hardware-UART sein, wenn man den aber ohnehin als Schnittstelle nutzt, ist es sinnvoll, diesen zu wählen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ    STX_PORT        = PORTD&lt;br /&gt;
.equ    STX_DDR         = DDRD&lt;br /&gt;
.equ    STX             = PD1&lt;br /&gt;
&lt;br /&gt;
.equ    SRX_PIN         = PIND&lt;br /&gt;
.equ    SRX_PORT        = PORTD&lt;br /&gt;
.equ    SRX             = PD0&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&#039;&#039;Beispiel für Hardware-UART im ATMega 8, 48, 168&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn man einen neuen Controller hinzufügen möchte, muss noch die BufferSize angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Compilieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Da der Bootloader in Assembler geschrieben ist, kann WinAVR nicht so ohne weiteres damit umgehen.&lt;br /&gt;
Das einfachste ist, sich das AVRStudio von Atmel herunterzuladen [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725]&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis AvrAssembler2 befindet sich der benötigte Assembler. Das Einfachste ist, sich die unter Appnotes benötigte Include Datei des jeweiligen Controllers (oder das komplette Verzeichnis) und die avrasm2.exe ins Verzeichnis des Bootloaders zu kopieren.&lt;br /&gt;
&lt;br /&gt;
Danach wird der Assembler aufgerufen (m8.asm für ATMega 8) und der Bootloader kompiliert:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
avrasm2 -fI m8.asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das AVRStudio lässt sich unter Linux auch mit WINE installieren (die IDE des AVR Studio 4 geht allerdings nicht).&lt;br /&gt;
Der Aufruf des Assemblers lautet dann:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wine avrasm2.exe -fI M8.ASM&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Brennen des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Der nun erzeugte Bootloader wird mit dem vorhanden ISP Dongle in den AVR gebrannt (Intel Hex Format):&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;avrdude&amp;quot; -p m8 -c avr910 -P com1 -U flash:w:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i&lt;br /&gt;
  -U flash:v:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i -y&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brennen mit AVRDude auf COM 1 und einem AVR910 kompatiblen Dongle (z.B. myAVR USB)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Einstellen der Fuses ==&lt;br /&gt;
&lt;br /&gt;
Damit der Bootloader zu Beginn gestartet wird, müssen die entsprechenden Fuses gesetzt werden. Möglich ist dies u.a. mit der AVRDude GUI.&lt;br /&gt;
* BOOTRST muss auf 0 gestellt werden (aktiviert den Bootloader)&lt;br /&gt;
* BOOTSZ muss auf 10 gestellt werden für 256 Worte&lt;br /&gt;
&lt;br /&gt;
Bei einigen Controllern wie z.B. dem ATMega 48 wird das Flag&lt;br /&gt;
* Selfprogramming enabled gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Fuses mit myAVR ändern ===&lt;br /&gt;
&lt;br /&gt;
Der myAVR Dongle unterstützt leider keine Fuses mit AVRDude. Abhilfe liefert hier jedoch das myAVR Worpad Plus. Die Demo-Version (reicht hierfür) kann unter&lt;br /&gt;
&lt;br /&gt;
[http://www.myavr.info/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.info/download/myAVR_WorkpadPLUS_Demo.exe]&lt;br /&gt;
&lt;br /&gt;
heruntergeladen werden. Nach dem Installieren und Starten im Menü Extras – Fuse- und Lock-Bits wählen. Das Programm ermittelt automatisch den aktuellen Status. Je nach Controller steht der Bootloader Support unter High oder Extended Fuses.&lt;br /&gt;
&lt;br /&gt;
== Brennen des eigentlichen Programmes ==&lt;br /&gt;
&lt;br /&gt;
Damit das Programm mit Peters Tool in den Controller geladen werden kann, muss im Intel HEX Format vorliegen.&lt;br /&gt;
Im makefile Template von WinAVR (und auch der ATMegaIDE) ist dies standardmäßig eingestellt.&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Output format. (can be srec, ihex, binary)&lt;br /&gt;
FORMAT = ihex&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach wird Peters Firmware-Update Tool aufgerufen (hier COM 2)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fboot /C2 /Pmain.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;hier wird die main.hex als Hauptprogramm gebrannt&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wichtig hierbei ist, dass der Dateiname der alten DOS Konvention entspricht. Also keine langen Dateinamen.&lt;br /&gt;
&lt;br /&gt;
Wenn noch keine Firmware im Controller ist (direkt nach Installieren des Bootloaders) startet der Brennvorgang automatisch. Andernfalls wartet das Programm auf den Reset des Controllers.&lt;br /&gt;
Peters Tool arbeitet nur mit COM1 bis COM4 zusammen. Also ggf. den USB Adapter im Gerätemanager umstellen.&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe sieht dann wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C:\DOCUME~1\zzzzzz\Desktop\avr\_soft\fboot18&amp;gt;fboot18 /Ptest.hex /b9600 /c1&lt;br /&gt;
COM 1 at 9600 Baud: Connected (One wire)&lt;br /&gt;
Bootloader V1.8&lt;br /&gt;
Target: 1E9108&lt;br /&gt;
Buffer: 32 Byte&lt;br /&gt;
Size available: 1534 Byte&lt;br /&gt;
Program test.hex: 00000 - 00073 successful&lt;br /&gt;
CRC: o.k.&lt;br /&gt;
Elapsed time: 1.43 seconds&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bootloader-Support in ATMegaIDE 2007 ==&lt;br /&gt;
&lt;br /&gt;
Die IDE kann wahlweise mit AVRDude und einem entsprechenden ISP-Dongle oder mit dem Bootloader das Programm in den Flash brennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anpassen des Programms ===&lt;br /&gt;
Um das Firmware Update komfortabler zu gestalten kann ein Software-Reset eingebaut werden, der per RS232/ USB Schnittstelle oder Tastendruck ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==== Tastendruck ====&lt;br /&gt;
In der mitgelieferten Standard.h gibt es den Befehl void Reset();. Er löst mit Hilfe des Watchdog-Timers einen Hardwarereset aus.&lt;br /&gt;
Man kann jetzt z.B. auf einen bestimmten Tastendruck (-kombination) hin diesen Reset ausführen.&lt;br /&gt;
&lt;br /&gt;
==== RS232/ USB ====&lt;br /&gt;
Alternativ kann ein entsprechender Befehl über die RS232 gesendet werden. Standardmäßig ist dies 0xFF ‚R‘.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Einstellungen in der IDE ===&lt;br /&gt;
&lt;br /&gt;
Der Bootloader Support kann bei den Projekt-Eigenschaften eingestellt werden. Beim Brennen wird er dann automatisch benutzt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== ToDo ==&lt;br /&gt;
&lt;br /&gt;
=== Support großer ATMegas ===&lt;br /&gt;
&lt;br /&gt;
Peter und andere arbeiten momentan noch an der Anpassung für größere Controller wie den ATMega 128.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komfortables Stand-Alone Firmware-Update ===&lt;br /&gt;
&lt;br /&gt;
Basierend auf dem Code der IDE wird es im September noch ein Standalone Programm für das Firmware-Update geben. Es wird auch ein entsprechendes Protokoll zur Versionskontrollle haben. (bisherige Soft- und viel wichtiger Hardwareversion wird gechecked). Eine Verschlüsselung der Firmware-Datei ist in Planung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Automatisches erzeugen des Booloaders ===&lt;br /&gt;
Sobald ich Zeit habe kommt in die IDE auch die Möglichkeit, die Anpassungen des Bootloaders von der IDE durchführen zu lassen.&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28213</id>
		<title>AVR Bootloader FastBoot von Peter Dannegger</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28213"/>
		<updated>2008-05-26T18:31:11Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Brennen des Bootloaders */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Karsten Donat&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Was ist ein Bootloader? ==&lt;br /&gt;
&lt;br /&gt;
Der Bootloader ist selbst ein kleines Programm. Es wird beim Start des Controllers zuerst ausgeführt. Damit das PC-Programm für das Firmware-Update sich melden kann, wartet der Bootloader  eine gewisse Zeit (hier 0,33 Sekunden) auf ein Zeichen über die serielle Schnittstelle (UART  RS232, USB). Kommt dies Zeichen wird die neue Firmware gebrannt. Andernfalls wird das eigentliche Programm des Controllers ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dem eigentlichen Anwendungsprogramm geht natürlich der Platz, den der Bootloader benötigt, verloren. Da Peters Bootloader jedoch in ein 512Bytes (256 Worte!) großes Segment passt, stört das nicht weiter.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird der Programmcode des Microcontrollers mit einem ISP-Dongle in den Flash gebrannt. Aus verschiedenen Gründen kann dies jedoch nicht möglich/ gewünscht sein:&lt;br /&gt;
* Speed: Der ISP-Dongle kann langsam sein (z.B. myAVR mit aktuellem AVRDude)&lt;br /&gt;
* PINs: Man braucht die PINs und will ISP abschalten ( Fuses, aber Vorsicht!, danach kann man nur mit dem Bootloader oder einem STK-500 noch an den Flash)&lt;br /&gt;
* Komfort: Man möchte dem Kunden/Nutzer die Möglichkeit geben, eine neue Firmware selbst einzuspielen. In der Regel hat dieser jedoch keinen ISP-Dongle zur Hand. Eine RS232- oder USB-Schnittstelle ist aber oftmals vorhanden.&lt;br /&gt;
* Sicherheit: Man möchte dem Kunden nicht die Firmware in deassemblierbarer Form geben (über geänderten Bootloader kann die Datei verschlüsselt sein).&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/73196 http://www.mikrocontroller.net/topic/73196] - Forum-Beitrag und Bootloader&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725] - Atmel AVR-Studio (Bootloader compilieren)&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR/Bootloader.pdf http://www.KarstenDonat.de/AVR/Bootloader.pdf] - diese Anleitung als PDF&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR http://www.KarstenDonat.de/AVR] - ATMega IDE 2007&lt;br /&gt;
* [http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion (Fuses beim myAVR ändern)&lt;br /&gt;
* [http://www.kreatives-chaos.com/artikel/fastboot17-frontend-python Python Implementierung des bootloaders]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt] - Protokollbeschreibung&lt;br /&gt;
&lt;br /&gt;
== Bootloader anpassen ==&lt;br /&gt;
&lt;br /&gt;
=== CPU-Frequenz, Wartezeit - FASTLOAD.H ===&lt;br /&gt;
&lt;br /&gt;
Bei XTAL die benutzte Frequenz des Controllers einstellen (jungfräuliche AVRs haben oft intern 1MHz aktiviert!).&lt;br /&gt;
Im Standard Makefile von WinAVR steht die Frequenz unter F_CPU&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	XTAL		= 3686400	; 8MHz, not critical&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die Wartezeit auf das Firmware-Update beim Booten anzupassen:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	BootDelay	= XTAL / 3	; 0.33s&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== UART Port/ Pins – M*.ASM ===&lt;br /&gt;
&lt;br /&gt;
Da die verwendeten Pins für die Schnittstelle frei wählbar sind, müssen diese noch eingestellt werden. Für einige Controller liegen bereits Definitionsdateien bei. Für den ATMega 8 heißt die Datei beispielsweise M8.ASM.&lt;br /&gt;
&lt;br /&gt;
STX_PORT: Hier den Sendeport angeben. Für den Hardware-UART des M8 wäre das Port D. Es muss zwar nicht der Hardware-UART sein, wenn man den aber ohnehin als Schnittstelle nutzt, ist es sinnvoll, diesen zu wählen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ    STX_PORT        = PORTD&lt;br /&gt;
.equ    STX_DDR         = DDRD&lt;br /&gt;
.equ    STX             = PD1&lt;br /&gt;
&lt;br /&gt;
.equ    SRX_PIN         = PIND&lt;br /&gt;
.equ    SRX_PORT        = PORTD&lt;br /&gt;
.equ    SRX             = PD0&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&#039;&#039;Beispiel für Hardware-UART im ATMega 8, 48, 168&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn man einen neuen Controller hinzufügen möchte, muss noch die BufferSize angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Compilieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Da der Bootloader in Assembler geschrieben ist, kann WinAVR nicht so ohne weiteres damit umgehen.&lt;br /&gt;
Das einfachste ist, sich das AVRStudio von Atmel herunterzuladen [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725]&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis AvrAssembler2 befindet sich der benötigte Assembler. Das Einfachste ist, sich die unter Appnotes benötigte Include Datei des jeweiligen Controllers (oder das komplette Verzeichnis) und die avrasm2.exe ins Verzeichnis des Bootloaders zu kopieren.&lt;br /&gt;
&lt;br /&gt;
Danach wird der Assembler aufgerufen (m8.asm für ATMega 8) und der Bootloader kompiliert:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
avrasm2 -fI m8.asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das AVRStudio lässt sich unter Linux auch mit WINE installieren (die IDE des AVR Studio 4 geht allerdings nicht).&lt;br /&gt;
Der Aufruf des Assemblers lautet dann:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wine avrasm2.exe -fI M8.ASM&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Brennen des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Der nun erzeugte Bootloader wird mit dem vorhanden ISP Dongle in den AVR gebrannt (Intel Hex Format):&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;avrdude&amp;quot; -p m8 -c avr910 -P com1 -U flash:w:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i&lt;br /&gt;
  -U flash:v:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i -y&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brennen mit AVRDude auf COM 1 und einem AVR910 kompatiblen Dongle (z.B. myAVR USB)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Einstellen der Fuses ==&lt;br /&gt;
&lt;br /&gt;
Damit der Bootloader zu Beginn gestartet wird, müssen die entsprechenden Fuses gesetzt werden. Möglich ist dies u.a. mit der AVRDude GUI.&lt;br /&gt;
* BOOTRST muss auf 0 gestellt werden (aktiviert den Bootloader)&lt;br /&gt;
* BOOTSZ muss auf 10 gestellt werden für 256 Worte&lt;br /&gt;
&lt;br /&gt;
Bei einigen Controllern wie z.B. dem ATMega 48 wird das Flag&lt;br /&gt;
* Selfprogramming enabled gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Fuses mit myAVR ändern ===&lt;br /&gt;
&lt;br /&gt;
Der myAVR Dongle unterstützt leider keine Fuses mit AVRDude. Abhilfe liefert hier jedoch das myAVR Worpad Plus. Die Demo-Version (reicht hierfür) kann unter&lt;br /&gt;
&lt;br /&gt;
[http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion&lt;br /&gt;
&lt;br /&gt;
heruntergeladen werden.&lt;br /&gt;
Nach dem Installieren und starten im Menü Extras – Fuse- und Lock-Bits wählen.&lt;br /&gt;
Das Programm ermittelt automatisch den aktuellen Status. Je nach Controller steht der Bootloader Support unter High oder Extended Fuses.&lt;br /&gt;
&lt;br /&gt;
== Brennen des eigentlichen Programmes ==&lt;br /&gt;
&lt;br /&gt;
Damit das Programm mit Peters Tool in den Controller geladen werden kann, muss im Intel HEX Format vorliegen.&lt;br /&gt;
Im makefile Template von WinAVR (und auch der ATMegaIDE) ist dies standardmäßig eingestellt.&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Output format. (can be srec, ihex, binary)&lt;br /&gt;
FORMAT = ihex&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach wird Peters Firmware-Update Tool aufgerufen (hier COM 2)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fboot /C2 /Pmain.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;hier wird die main.hex als Hauptprogramm gebrannt&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wichtig hierbei ist, dass der Dateiname der alten DOS Konvention entspricht. Also keine langen Dateinamen.&lt;br /&gt;
&lt;br /&gt;
Wenn noch keine Firmware im Controller ist (direkt nach Installieren des Bootloaders) startet der Brennvorgang automatisch. Andernfalls wartet das Programm auf den Reset des Controllers.&lt;br /&gt;
Peters Tool arbeitet nur mit COM1 bis COM4 zusammen. Also ggf. den USB Adapter im Gerätemanager umstellen.&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe sieht dann wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C:\DOCUME~1\zzzzzz\Desktop\avr\_soft\fboot18&amp;gt;fboot18 /Ptest.hex /b9600 /c1&lt;br /&gt;
COM 1 at 9600 Baud: Connected (One wire)&lt;br /&gt;
Bootloader V1.8&lt;br /&gt;
Target: 1E9108&lt;br /&gt;
Buffer: 32 Byte&lt;br /&gt;
Size available: 1534 Byte&lt;br /&gt;
Program test.hex: 00000 - 00073 successful&lt;br /&gt;
CRC: o.k.&lt;br /&gt;
Elapsed time: 1.43 seconds&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bootloader-Support in ATMegaIDE 2007 ==&lt;br /&gt;
&lt;br /&gt;
Die IDE kann wahlweise mit AVRDude und einem entsprechenden ISP-Dongle oder mit dem Bootloader das Programm in den Flash brennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anpassen des Programms ===&lt;br /&gt;
Um das Firmware Update komfortabler zu gestalten kann ein Software-Reset eingebaut werden, der per RS232/ USB Schnittstelle oder Tastendruck ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==== Tastendruck ====&lt;br /&gt;
In der mitgelieferten Standard.h gibt es den Befehl void Reset();. Er löst mit Hilfe des Watchdog-Timers einen Hardwarereset aus.&lt;br /&gt;
Man kann jetzt z.B. auf einen bestimmten Tastendruck (-kombination) hin diesen Reset ausführen.&lt;br /&gt;
&lt;br /&gt;
==== RS232/ USB ====&lt;br /&gt;
Alternativ kann ein entsprechender Befehl über die RS232 gesendet werden. Standardmäßig ist dies 0xFF ‚R‘.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Einstellungen in der IDE ===&lt;br /&gt;
&lt;br /&gt;
Der Bootloader Support kann bei den Projekt-Eigenschaften eingestellt werden. Beim Brennen wird er dann automatisch benutzt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== ToDo ==&lt;br /&gt;
&lt;br /&gt;
=== Support großer ATMegas ===&lt;br /&gt;
&lt;br /&gt;
Peter und andere arbeiten momentan noch an der Anpassung für größere Controller wie den ATMega 128.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komfortables Stand-Alone Firmware-Update ===&lt;br /&gt;
&lt;br /&gt;
Basierend auf dem Code der IDE wird es im September noch ein Standalone Programm für das Firmware-Update geben. Es wird auch ein entsprechendes Protokoll zur Versionskontrollle haben. (bisherige Soft- und viel wichtiger Hardwareversion wird gechecked). Eine Verschlüsselung der Firmware-Datei ist in Planung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Automatisches erzeugen des Booloaders ===&lt;br /&gt;
Sobald ich Zeit habe kommt in die IDE auch die Möglichkeit, die Anpassungen des Bootloaders von der IDE durchführen zu lassen.&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28212</id>
		<title>AVR Bootloader FastBoot von Peter Dannegger</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28212"/>
		<updated>2008-05-26T18:30:42Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Brennen des Bootloaders */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Karsten Donat&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Was ist ein Bootloader? ==&lt;br /&gt;
&lt;br /&gt;
Der Bootloader ist selbst ein kleines Programm. Es wird beim Start des Controllers zuerst ausgeführt. Damit das PC-Programm für das Firmware-Update sich melden kann, wartet der Bootloader  eine gewisse Zeit (hier 0,33 Sekunden) auf ein Zeichen über die serielle Schnittstelle (UART  RS232, USB). Kommt dies Zeichen wird die neue Firmware gebrannt. Andernfalls wird das eigentliche Programm des Controllers ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dem eigentlichen Anwendungsprogramm geht natürlich der Platz, den der Bootloader benötigt, verloren. Da Peters Bootloader jedoch in ein 512Bytes (256 Worte!) großes Segment passt, stört das nicht weiter.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird der Programmcode des Microcontrollers mit einem ISP-Dongle in den Flash gebrannt. Aus verschiedenen Gründen kann dies jedoch nicht möglich/ gewünscht sein:&lt;br /&gt;
* Speed: Der ISP-Dongle kann langsam sein (z.B. myAVR mit aktuellem AVRDude)&lt;br /&gt;
* PINs: Man braucht die PINs und will ISP abschalten ( Fuses, aber Vorsicht!, danach kann man nur mit dem Bootloader oder einem STK-500 noch an den Flash)&lt;br /&gt;
* Komfort: Man möchte dem Kunden/Nutzer die Möglichkeit geben, eine neue Firmware selbst einzuspielen. In der Regel hat dieser jedoch keinen ISP-Dongle zur Hand. Eine RS232- oder USB-Schnittstelle ist aber oftmals vorhanden.&lt;br /&gt;
* Sicherheit: Man möchte dem Kunden nicht die Firmware in deassemblierbarer Form geben (über geänderten Bootloader kann die Datei verschlüsselt sein).&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/73196 http://www.mikrocontroller.net/topic/73196] - Forum-Beitrag und Bootloader&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725] - Atmel AVR-Studio (Bootloader compilieren)&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR/Bootloader.pdf http://www.KarstenDonat.de/AVR/Bootloader.pdf] - diese Anleitung als PDF&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR http://www.KarstenDonat.de/AVR] - ATMega IDE 2007&lt;br /&gt;
* [http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion (Fuses beim myAVR ändern)&lt;br /&gt;
* [http://www.kreatives-chaos.com/artikel/fastboot17-frontend-python Python Implementierung des bootloaders]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt] - Protokollbeschreibung&lt;br /&gt;
&lt;br /&gt;
== Bootloader anpassen ==&lt;br /&gt;
&lt;br /&gt;
=== CPU-Frequenz, Wartezeit - FASTLOAD.H ===&lt;br /&gt;
&lt;br /&gt;
Bei XTAL die benutzte Frequenz des Controllers einstellen (jungfräuliche AVRs haben oft intern 1MHz aktiviert!).&lt;br /&gt;
Im Standard Makefile von WinAVR steht die Frequenz unter F_CPU&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	XTAL		= 3686400	; 8MHz, not critical&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die Wartezeit auf das Firmware-Update beim Booten anzupassen:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	BootDelay	= XTAL / 3	; 0.33s&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== UART Port/ Pins – M*.ASM ===&lt;br /&gt;
&lt;br /&gt;
Da die verwendeten Pins für die Schnittstelle frei wählbar sind, müssen diese noch eingestellt werden. Für einige Controller liegen bereits Definitionsdateien bei. Für den ATMega 8 heißt die Datei beispielsweise M8.ASM.&lt;br /&gt;
&lt;br /&gt;
STX_PORT: Hier den Sendeport angeben. Für den Hardware-UART des M8 wäre das Port D. Es muss zwar nicht der Hardware-UART sein, wenn man den aber ohnehin als Schnittstelle nutzt, ist es sinnvoll, diesen zu wählen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ    STX_PORT        = PORTD&lt;br /&gt;
.equ    STX_DDR         = DDRD&lt;br /&gt;
.equ    STX             = PD1&lt;br /&gt;
&lt;br /&gt;
.equ    SRX_PIN         = PIND&lt;br /&gt;
.equ    SRX_PORT        = PORTD&lt;br /&gt;
.equ    SRX             = PD0&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&#039;&#039;Beispiel für Hardware-UART im ATMega 8, 48, 168&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn man einen neuen Controller hinzufügen möchte, muss noch die BufferSize angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Compilieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Da der Bootloader in Assembler geschrieben ist, kann WinAVR nicht so ohne weiteres damit umgehen.&lt;br /&gt;
Das einfachste ist, sich das AVRStudio von Atmel herunterzuladen [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725]&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis AvrAssembler2 befindet sich der benötigte Assembler. Das Einfachste ist, sich die unter Appnotes benötigte Include Datei des jeweiligen Controllers (oder das komplette Verzeichnis) und die avrasm2.exe ins Verzeichnis des Bootloaders zu kopieren.&lt;br /&gt;
&lt;br /&gt;
Danach wird der Assembler aufgerufen (m8.asm für ATMega 8) und der Bootloader kompiliert:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
avrasm2 -fI m8.asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das AVRStudio lässt sich unter Linux auch mit WINE installieren (die IDE des AVR Studio 4 geht allerdings nicht).&lt;br /&gt;
Der Aufruf des Assemblers lautet dann:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wine avrasm2.exe -fI M8.ASM&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Brennen des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Der nun erzeugte Bootloader wird mit dem vorhanden ISP Dongle in den AVR gebrannt (Intel Hex Format):&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;avrdude&amp;quot; -p m8 -c avr910 -P com1 -U flash:w:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i&lt;br /&gt;
  -U flash:v:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i -y&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brennen mit AVRDude auf COM 1 und einem AVR910 kompatiblen Dongle (z.B. myAVR USB)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== Einstellen der Fuses ==&lt;br /&gt;
&lt;br /&gt;
Damit der Bootloader zu Beginn gestartet wird, müssen die entsprechenden Fuses gesetzt werden. Möglich ist dies u.a. mit der AVRDude GUI.&lt;br /&gt;
* BOOTRST muss auf 0 gestellt werden (aktiviert den Bootloader)&lt;br /&gt;
* BOOTSZ muss auf 10 gestellt werden für 256 Worte&lt;br /&gt;
&lt;br /&gt;
Bei einigen Controllern wie z.B. dem ATMega 48 wird das Flag&lt;br /&gt;
* Selfprogramming enabled gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Fuses mit myAVR ändern ===&lt;br /&gt;
&lt;br /&gt;
Der myAVR Dongle unterstützt leider keine Fuses mit AVRDude. Abhilfe liefert hier jedoch das myAVR Worpad Plus. Die Demo-Version (reicht hierfür) kann unter&lt;br /&gt;
&lt;br /&gt;
[http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion&lt;br /&gt;
&lt;br /&gt;
heruntergeladen werden.&lt;br /&gt;
Nach dem Installieren und starten im Menü Extras – Fuse- und Lock-Bits wählen.&lt;br /&gt;
Das Programm ermittelt automatisch den aktuellen Status. Je nach Controller steht der Bootloader Support unter High oder Extended Fuses.&lt;br /&gt;
&lt;br /&gt;
== Brennen des eigentlichen Programmes ==&lt;br /&gt;
&lt;br /&gt;
Damit das Programm mit Peters Tool in den Controller geladen werden kann, muss im Intel HEX Format vorliegen.&lt;br /&gt;
Im makefile Template von WinAVR (und auch der ATMegaIDE) ist dies standardmäßig eingestellt.&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Output format. (can be srec, ihex, binary)&lt;br /&gt;
FORMAT = ihex&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach wird Peters Firmware-Update Tool aufgerufen (hier COM 2)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fboot /C2 /Pmain.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;hier wird die main.hex als Hauptprogramm gebrannt&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wichtig hierbei ist, dass der Dateiname der alten DOS Konvention entspricht. Also keine langen Dateinamen.&lt;br /&gt;
&lt;br /&gt;
Wenn noch keine Firmware im Controller ist (direkt nach Installieren des Bootloaders) startet der Brennvorgang automatisch. Andernfalls wartet das Programm auf den Reset des Controllers.&lt;br /&gt;
Peters Tool arbeitet nur mit COM1 bis COM4 zusammen. Also ggf. den USB Adapter im Gerätemanager umstellen.&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe sieht dann wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C:\DOCUME~1\zzzzzz\Desktop\avr\_soft\fboot18&amp;gt;fboot18 /Ptest.hex /b9600 /c1&lt;br /&gt;
COM 1 at 9600 Baud: Connected (One wire)&lt;br /&gt;
Bootloader V1.8&lt;br /&gt;
Target: 1E9108&lt;br /&gt;
Buffer: 32 Byte&lt;br /&gt;
Size available: 1534 Byte&lt;br /&gt;
Program test.hex: 00000 - 00073 successful&lt;br /&gt;
CRC: o.k.&lt;br /&gt;
Elapsed time: 1.43 seconds&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bootloader-Support in ATMegaIDE 2007 ==&lt;br /&gt;
&lt;br /&gt;
Die IDE kann wahlweise mit AVRDude und einem entsprechenden ISP-Dongle oder mit dem Bootloader das Programm in den Flash brennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anpassen des Programms ===&lt;br /&gt;
Um das Firmware Update komfortabler zu gestalten kann ein Software-Reset eingebaut werden, der per RS232/ USB Schnittstelle oder Tastendruck ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==== Tastendruck ====&lt;br /&gt;
In der mitgelieferten Standard.h gibt es den Befehl void Reset();. Er löst mit Hilfe des Watchdog-Timers einen Hardwarereset aus.&lt;br /&gt;
Man kann jetzt z.B. auf einen bestimmten Tastendruck (-kombination) hin diesen Reset ausführen.&lt;br /&gt;
&lt;br /&gt;
==== RS232/ USB ====&lt;br /&gt;
Alternativ kann ein entsprechender Befehl über die RS232 gesendet werden. Standardmäßig ist dies 0xFF ‚R‘.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Einstellungen in der IDE ===&lt;br /&gt;
&lt;br /&gt;
Der Bootloader Support kann bei den Projekt-Eigenschaften eingestellt werden. Beim Brennen wird er dann automatisch benutzt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== ToDo ==&lt;br /&gt;
&lt;br /&gt;
=== Support großer ATMegas ===&lt;br /&gt;
&lt;br /&gt;
Peter und andere arbeiten momentan noch an der Anpassung für größere Controller wie den ATMega 128.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komfortables Stand-Alone Firmware-Update ===&lt;br /&gt;
&lt;br /&gt;
Basierend auf dem Code der IDE wird es im September noch ein Standalone Programm für das Firmware-Update geben. Es wird auch ein entsprechendes Protokoll zur Versionskontrollle haben. (bisherige Soft- und viel wichtiger Hardwareversion wird gechecked). Eine Verschlüsselung der Firmware-Datei ist in Planung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Automatisches erzeugen des Booloaders ===&lt;br /&gt;
Sobald ich Zeit habe kommt in die IDE auch die Möglichkeit, die Anpassungen des Bootloaders von der IDE durchführen zu lassen.&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28211</id>
		<title>AVR Bootloader FastBoot von Peter Dannegger</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28211"/>
		<updated>2008-05-26T18:29:32Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Compilieren des Bootloaders */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Karsten Donat&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Was ist ein Bootloader? ==&lt;br /&gt;
&lt;br /&gt;
Der Bootloader ist selbst ein kleines Programm. Es wird beim Start des Controllers zuerst ausgeführt. Damit das PC-Programm für das Firmware-Update sich melden kann, wartet der Bootloader  eine gewisse Zeit (hier 0,33 Sekunden) auf ein Zeichen über die serielle Schnittstelle (UART  RS232, USB). Kommt dies Zeichen wird die neue Firmware gebrannt. Andernfalls wird das eigentliche Programm des Controllers ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dem eigentlichen Anwendungsprogramm geht natürlich der Platz, den der Bootloader benötigt, verloren. Da Peters Bootloader jedoch in ein 512Bytes (256 Worte!) großes Segment passt, stört das nicht weiter.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird der Programmcode des Microcontrollers mit einem ISP-Dongle in den Flash gebrannt. Aus verschiedenen Gründen kann dies jedoch nicht möglich/ gewünscht sein:&lt;br /&gt;
* Speed: Der ISP-Dongle kann langsam sein (z.B. myAVR mit aktuellem AVRDude)&lt;br /&gt;
* PINs: Man braucht die PINs und will ISP abschalten ( Fuses, aber Vorsicht!, danach kann man nur mit dem Bootloader oder einem STK-500 noch an den Flash)&lt;br /&gt;
* Komfort: Man möchte dem Kunden/Nutzer die Möglichkeit geben, eine neue Firmware selbst einzuspielen. In der Regel hat dieser jedoch keinen ISP-Dongle zur Hand. Eine RS232- oder USB-Schnittstelle ist aber oftmals vorhanden.&lt;br /&gt;
* Sicherheit: Man möchte dem Kunden nicht die Firmware in deassemblierbarer Form geben (über geänderten Bootloader kann die Datei verschlüsselt sein).&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/73196 http://www.mikrocontroller.net/topic/73196] - Forum-Beitrag und Bootloader&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725] - Atmel AVR-Studio (Bootloader compilieren)&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR/Bootloader.pdf http://www.KarstenDonat.de/AVR/Bootloader.pdf] - diese Anleitung als PDF&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR http://www.KarstenDonat.de/AVR] - ATMega IDE 2007&lt;br /&gt;
* [http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion (Fuses beim myAVR ändern)&lt;br /&gt;
* [http://www.kreatives-chaos.com/artikel/fastboot17-frontend-python Python Implementierung des bootloaders]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt] - Protokollbeschreibung&lt;br /&gt;
&lt;br /&gt;
== Bootloader anpassen ==&lt;br /&gt;
&lt;br /&gt;
=== CPU-Frequenz, Wartezeit - FASTLOAD.H ===&lt;br /&gt;
&lt;br /&gt;
Bei XTAL die benutzte Frequenz des Controllers einstellen (jungfräuliche AVRs haben oft intern 1MHz aktiviert!).&lt;br /&gt;
Im Standard Makefile von WinAVR steht die Frequenz unter F_CPU&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	XTAL		= 3686400	; 8MHz, not critical&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die Wartezeit auf das Firmware-Update beim Booten anzupassen:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	BootDelay	= XTAL / 3	; 0.33s&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== UART Port/ Pins – M*.ASM ===&lt;br /&gt;
&lt;br /&gt;
Da die verwendeten Pins für die Schnittstelle frei wählbar sind, müssen diese noch eingestellt werden. Für einige Controller liegen bereits Definitionsdateien bei. Für den ATMega 8 heißt die Datei beispielsweise M8.ASM.&lt;br /&gt;
&lt;br /&gt;
STX_PORT: Hier den Sendeport angeben. Für den Hardware-UART des M8 wäre das Port D. Es muss zwar nicht der Hardware-UART sein, wenn man den aber ohnehin als Schnittstelle nutzt, ist es sinnvoll, diesen zu wählen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ    STX_PORT        = PORTD&lt;br /&gt;
.equ    STX_DDR         = DDRD&lt;br /&gt;
.equ    STX             = PD1&lt;br /&gt;
&lt;br /&gt;
.equ    SRX_PIN         = PIND&lt;br /&gt;
.equ    SRX_PORT        = PORTD&lt;br /&gt;
.equ    SRX             = PD0&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&#039;&#039;Beispiel für Hardware-UART im ATMega 8, 48, 168&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn man einen neuen Controller hinzufügen möchte, muss noch die BufferSize angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Compilieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Da der Bootloader in Assembler geschrieben ist, kann WinAVR nicht so ohne weiteres damit umgehen.&lt;br /&gt;
Das einfachste ist, sich das AVRStudio von Atmel herunterzuladen [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725]&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis AvrAssembler2 befindet sich der benötigte Assembler. Das Einfachste ist, sich die unter Appnotes benötigte Include Datei des jeweiligen Controllers (oder das komplette Verzeichnis) und die avrasm2.exe ins Verzeichnis des Bootloaders zu kopieren.&lt;br /&gt;
&lt;br /&gt;
Danach wird der Assembler aufgerufen (m8.asm für ATMega 8) und der Bootloader kompiliert:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
avrasm2 -fI m8.asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das AVRStudio lässt sich unter Linux auch mit WINE installieren (die IDE des AVR Studio 4 geht allerdings nicht).&lt;br /&gt;
Der Aufruf des Assemblers lautet dann:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wine avrasm2.exe -fI M8.ASM&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Brennen des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Der nun erzeugte Bootloader wird mit dem vorhanden ISP Dongle in den AVR gebrannt (Intel Hex Format)&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;avrdude&amp;quot; -p m8 -c avr910 -P com1 -U flash:w:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i&lt;br /&gt;
  -U flash:v:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i -y&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brennen mit AVRDude auf COM 1 und einem AVR910 kompatiblen Dongle (z.B. myAVR USB)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== Einstellen der Fuses ==&lt;br /&gt;
&lt;br /&gt;
Damit der Bootloader zu Beginn gestartet wird, müssen die entsprechenden Fuses gesetzt werden. Möglich ist dies u.a. mit der AVRDude GUI.&lt;br /&gt;
* BOOTRST muss auf 0 gestellt werden (aktiviert den Bootloader)&lt;br /&gt;
* BOOTSZ muss auf 10 gestellt werden für 256 Worte&lt;br /&gt;
&lt;br /&gt;
Bei einigen Controllern wie z.B. dem ATMega 48 wird das Flag&lt;br /&gt;
* Selfprogramming enabled gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Fuses mit myAVR ändern ===&lt;br /&gt;
&lt;br /&gt;
Der myAVR Dongle unterstützt leider keine Fuses mit AVRDude. Abhilfe liefert hier jedoch das myAVR Worpad Plus. Die Demo-Version (reicht hierfür) kann unter&lt;br /&gt;
&lt;br /&gt;
[http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion&lt;br /&gt;
&lt;br /&gt;
heruntergeladen werden.&lt;br /&gt;
Nach dem Installieren und starten im Menü Extras – Fuse- und Lock-Bits wählen.&lt;br /&gt;
Das Programm ermittelt automatisch den aktuellen Status. Je nach Controller steht der Bootloader Support unter High oder Extended Fuses.&lt;br /&gt;
&lt;br /&gt;
== Brennen des eigentlichen Programmes ==&lt;br /&gt;
&lt;br /&gt;
Damit das Programm mit Peters Tool in den Controller geladen werden kann, muss im Intel HEX Format vorliegen.&lt;br /&gt;
Im makefile Template von WinAVR (und auch der ATMegaIDE) ist dies standardmäßig eingestellt.&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Output format. (can be srec, ihex, binary)&lt;br /&gt;
FORMAT = ihex&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach wird Peters Firmware-Update Tool aufgerufen (hier COM 2)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fboot /C2 /Pmain.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;hier wird die main.hex als Hauptprogramm gebrannt&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wichtig hierbei ist, dass der Dateiname der alten DOS Konvention entspricht. Also keine langen Dateinamen.&lt;br /&gt;
&lt;br /&gt;
Wenn noch keine Firmware im Controller ist (direkt nach Installieren des Bootloaders) startet der Brennvorgang automatisch. Andernfalls wartet das Programm auf den Reset des Controllers.&lt;br /&gt;
Peters Tool arbeitet nur mit COM1 bis COM4 zusammen. Also ggf. den USB Adapter im Gerätemanager umstellen.&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe sieht dann wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C:\DOCUME~1\zzzzzz\Desktop\avr\_soft\fboot18&amp;gt;fboot18 /Ptest.hex /b9600 /c1&lt;br /&gt;
COM 1 at 9600 Baud: Connected (One wire)&lt;br /&gt;
Bootloader V1.8&lt;br /&gt;
Target: 1E9108&lt;br /&gt;
Buffer: 32 Byte&lt;br /&gt;
Size available: 1534 Byte&lt;br /&gt;
Program test.hex: 00000 - 00073 successful&lt;br /&gt;
CRC: o.k.&lt;br /&gt;
Elapsed time: 1.43 seconds&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bootloader-Support in ATMegaIDE 2007 ==&lt;br /&gt;
&lt;br /&gt;
Die IDE kann wahlweise mit AVRDude und einem entsprechenden ISP-Dongle oder mit dem Bootloader das Programm in den Flash brennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anpassen des Programms ===&lt;br /&gt;
Um das Firmware Update komfortabler zu gestalten kann ein Software-Reset eingebaut werden, der per RS232/ USB Schnittstelle oder Tastendruck ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==== Tastendruck ====&lt;br /&gt;
In der mitgelieferten Standard.h gibt es den Befehl void Reset();. Er löst mit Hilfe des Watchdog-Timers einen Hardwarereset aus.&lt;br /&gt;
Man kann jetzt z.B. auf einen bestimmten Tastendruck (-kombination) hin diesen Reset ausführen.&lt;br /&gt;
&lt;br /&gt;
==== RS232/ USB ====&lt;br /&gt;
Alternativ kann ein entsprechender Befehl über die RS232 gesendet werden. Standardmäßig ist dies 0xFF ‚R‘.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Einstellungen in der IDE ===&lt;br /&gt;
&lt;br /&gt;
Der Bootloader Support kann bei den Projekt-Eigenschaften eingestellt werden. Beim Brennen wird er dann automatisch benutzt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== ToDo ==&lt;br /&gt;
&lt;br /&gt;
=== Support großer ATMegas ===&lt;br /&gt;
&lt;br /&gt;
Peter und andere arbeiten momentan noch an der Anpassung für größere Controller wie den ATMega 128.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komfortables Stand-Alone Firmware-Update ===&lt;br /&gt;
&lt;br /&gt;
Basierend auf dem Code der IDE wird es im September noch ein Standalone Programm für das Firmware-Update geben. Es wird auch ein entsprechendes Protokoll zur Versionskontrollle haben. (bisherige Soft- und viel wichtiger Hardwareversion wird gechecked). Eine Verschlüsselung der Firmware-Datei ist in Planung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Automatisches erzeugen des Booloaders ===&lt;br /&gt;
Sobald ich Zeit habe kommt in die IDE auch die Möglichkeit, die Anpassungen des Bootloaders von der IDE durchführen zu lassen.&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28210</id>
		<title>AVR Bootloader FastBoot von Peter Dannegger</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28210"/>
		<updated>2008-05-26T18:26:59Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* UART Port/ Pins – M*.ASM */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Karsten Donat&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Was ist ein Bootloader? ==&lt;br /&gt;
&lt;br /&gt;
Der Bootloader ist selbst ein kleines Programm. Es wird beim Start des Controllers zuerst ausgeführt. Damit das PC-Programm für das Firmware-Update sich melden kann, wartet der Bootloader  eine gewisse Zeit (hier 0,33 Sekunden) auf ein Zeichen über die serielle Schnittstelle (UART  RS232, USB). Kommt dies Zeichen wird die neue Firmware gebrannt. Andernfalls wird das eigentliche Programm des Controllers ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dem eigentlichen Anwendungsprogramm geht natürlich der Platz, den der Bootloader benötigt, verloren. Da Peters Bootloader jedoch in ein 512Bytes (256 Worte!) großes Segment passt, stört das nicht weiter.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird der Programmcode des Microcontrollers mit einem ISP-Dongle in den Flash gebrannt. Aus verschiedenen Gründen kann dies jedoch nicht möglich/ gewünscht sein:&lt;br /&gt;
* Speed: Der ISP-Dongle kann langsam sein (z.B. myAVR mit aktuellem AVRDude)&lt;br /&gt;
* PINs: Man braucht die PINs und will ISP abschalten ( Fuses, aber Vorsicht!, danach kann man nur mit dem Bootloader oder einem STK-500 noch an den Flash)&lt;br /&gt;
* Komfort: Man möchte dem Kunden/Nutzer die Möglichkeit geben, eine neue Firmware selbst einzuspielen. In der Regel hat dieser jedoch keinen ISP-Dongle zur Hand. Eine RS232- oder USB-Schnittstelle ist aber oftmals vorhanden.&lt;br /&gt;
* Sicherheit: Man möchte dem Kunden nicht die Firmware in deassemblierbarer Form geben (über geänderten Bootloader kann die Datei verschlüsselt sein).&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/73196 http://www.mikrocontroller.net/topic/73196] - Forum-Beitrag und Bootloader&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725] - Atmel AVR-Studio (Bootloader compilieren)&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR/Bootloader.pdf http://www.KarstenDonat.de/AVR/Bootloader.pdf] - diese Anleitung als PDF&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR http://www.KarstenDonat.de/AVR] - ATMega IDE 2007&lt;br /&gt;
* [http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion (Fuses beim myAVR ändern)&lt;br /&gt;
* [http://www.kreatives-chaos.com/artikel/fastboot17-frontend-python Python Implementierung des bootloaders]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt] - Protokollbeschreibung&lt;br /&gt;
&lt;br /&gt;
== Bootloader anpassen ==&lt;br /&gt;
&lt;br /&gt;
=== CPU-Frequenz, Wartezeit - FASTLOAD.H ===&lt;br /&gt;
&lt;br /&gt;
Bei XTAL die benutzte Frequenz des Controllers einstellen (jungfräuliche AVRs haben oft intern 1MHz aktiviert!).&lt;br /&gt;
Im Standard Makefile von WinAVR steht die Frequenz unter F_CPU&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	XTAL		= 3686400	; 8MHz, not critical&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die Wartezeit auf das Firmware-Update beim Booten anzupassen:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	BootDelay	= XTAL / 3	; 0.33s&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== UART Port/ Pins – M*.ASM ===&lt;br /&gt;
&lt;br /&gt;
Da die verwendeten Pins für die Schnittstelle frei wählbar sind, müssen diese noch eingestellt werden. Für einige Controller liegen bereits Definitionsdateien bei. Für den ATMega 8 heißt die Datei beispielsweise M8.ASM.&lt;br /&gt;
&lt;br /&gt;
STX_PORT: Hier den Sendeport angeben. Für den Hardware-UART des M8 wäre das Port D. Es muss zwar nicht der Hardware-UART sein, wenn man den aber ohnehin als Schnittstelle nutzt, ist es sinnvoll, diesen zu wählen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ    STX_PORT        = PORTD&lt;br /&gt;
.equ    STX_DDR         = DDRD&lt;br /&gt;
.equ    STX             = PD1&lt;br /&gt;
&lt;br /&gt;
.equ    SRX_PIN         = PIND&lt;br /&gt;
.equ    SRX_PORT        = PORTD&lt;br /&gt;
.equ    SRX             = PD0&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&#039;&#039;Beispiel für Hardware-UART im ATMega 8, 48, 168&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn man einen neuen Controller hinzufügen möchte, muss noch die BufferSize angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Compilieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Da der Bootloader in Assembler geschrieben ist, kann WinAVR nicht so ohne weiteres damit umgehen.&lt;br /&gt;
Das einfachste ist, sich das AVRStudio von Atmel herunterzuladen [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725]&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis AvrAssembler2 befindet sich der benötigte Assembler. Das Einfachste ist, sich die unter Appnotes benötigte Include Datei des jeweiligen Controllers (oder das komplette Verzeichnis) und die avrasm2.exe ins Verzeichnis des Bootloaders zu kopieren.&lt;br /&gt;
&lt;br /&gt;
Danach wird der Assembler aufgerufen (m8.asm für ATMega 8)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
avrasm2 -fI m8.asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;Bootloader compilieren&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Das AVRStudio lässt sich unter Linux auch mit WINE installieren (die IDE des AVR Studio 4 geht allerdings nicht).&lt;br /&gt;
Der Aufruf des Assemblers ist dann:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wine avrasm2.exe -fI M8.ASM&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;Bootloader mit Hilfe von WINE compilieren&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Brennen des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Der nun erzeugte Bootloader wird mit dem vorhanden ISP Dongle in den AVR gebrannt (Intel Hex Format)&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;avrdude&amp;quot; -p m8 -c avr910 -P com1 -U flash:w:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i&lt;br /&gt;
  -U flash:v:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i -y&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brennen mit AVRDude auf COM 1 und einem AVR910 kompatiblen Dongle (z.B. myAVR USB)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== Einstellen der Fuses ==&lt;br /&gt;
&lt;br /&gt;
Damit der Bootloader zu Beginn gestartet wird, müssen die entsprechenden Fuses gesetzt werden. Möglich ist dies u.a. mit der AVRDude GUI.&lt;br /&gt;
* BOOTRST muss auf 0 gestellt werden (aktiviert den Bootloader)&lt;br /&gt;
* BOOTSZ muss auf 10 gestellt werden für 256 Worte&lt;br /&gt;
&lt;br /&gt;
Bei einigen Controllern wie z.B. dem ATMega 48 wird das Flag&lt;br /&gt;
* Selfprogramming enabled gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Fuses mit myAVR ändern ===&lt;br /&gt;
&lt;br /&gt;
Der myAVR Dongle unterstützt leider keine Fuses mit AVRDude. Abhilfe liefert hier jedoch das myAVR Worpad Plus. Die Demo-Version (reicht hierfür) kann unter&lt;br /&gt;
&lt;br /&gt;
[http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion&lt;br /&gt;
&lt;br /&gt;
heruntergeladen werden.&lt;br /&gt;
Nach dem Installieren und starten im Menü Extras – Fuse- und Lock-Bits wählen.&lt;br /&gt;
Das Programm ermittelt automatisch den aktuellen Status. Je nach Controller steht der Bootloader Support unter High oder Extended Fuses.&lt;br /&gt;
&lt;br /&gt;
== Brennen des eigentlichen Programmes ==&lt;br /&gt;
&lt;br /&gt;
Damit das Programm mit Peters Tool in den Controller geladen werden kann, muss im Intel HEX Format vorliegen.&lt;br /&gt;
Im makefile Template von WinAVR (und auch der ATMegaIDE) ist dies standardmäßig eingestellt.&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Output format. (can be srec, ihex, binary)&lt;br /&gt;
FORMAT = ihex&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach wird Peters Firmware-Update Tool aufgerufen (hier COM 2)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fboot /C2 /Pmain.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;hier wird die main.hex als Hauptprogramm gebrannt&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wichtig hierbei ist, dass der Dateiname der alten DOS Konvention entspricht. Also keine langen Dateinamen.&lt;br /&gt;
&lt;br /&gt;
Wenn noch keine Firmware im Controller ist (direkt nach Installieren des Bootloaders) startet der Brennvorgang automatisch. Andernfalls wartet das Programm auf den Reset des Controllers.&lt;br /&gt;
Peters Tool arbeitet nur mit COM1 bis COM4 zusammen. Also ggf. den USB Adapter im Gerätemanager umstellen.&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe sieht dann wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C:\DOCUME~1\zzzzzz\Desktop\avr\_soft\fboot18&amp;gt;fboot18 /Ptest.hex /b9600 /c1&lt;br /&gt;
COM 1 at 9600 Baud: Connected (One wire)&lt;br /&gt;
Bootloader V1.8&lt;br /&gt;
Target: 1E9108&lt;br /&gt;
Buffer: 32 Byte&lt;br /&gt;
Size available: 1534 Byte&lt;br /&gt;
Program test.hex: 00000 - 00073 successful&lt;br /&gt;
CRC: o.k.&lt;br /&gt;
Elapsed time: 1.43 seconds&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bootloader-Support in ATMegaIDE 2007 ==&lt;br /&gt;
&lt;br /&gt;
Die IDE kann wahlweise mit AVRDude und einem entsprechenden ISP-Dongle oder mit dem Bootloader das Programm in den Flash brennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anpassen des Programms ===&lt;br /&gt;
Um das Firmware Update komfortabler zu gestalten kann ein Software-Reset eingebaut werden, der per RS232/ USB Schnittstelle oder Tastendruck ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==== Tastendruck ====&lt;br /&gt;
In der mitgelieferten Standard.h gibt es den Befehl void Reset();. Er löst mit Hilfe des Watchdog-Timers einen Hardwarereset aus.&lt;br /&gt;
Man kann jetzt z.B. auf einen bestimmten Tastendruck (-kombination) hin diesen Reset ausführen.&lt;br /&gt;
&lt;br /&gt;
==== RS232/ USB ====&lt;br /&gt;
Alternativ kann ein entsprechender Befehl über die RS232 gesendet werden. Standardmäßig ist dies 0xFF ‚R‘.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Einstellungen in der IDE ===&lt;br /&gt;
&lt;br /&gt;
Der Bootloader Support kann bei den Projekt-Eigenschaften eingestellt werden. Beim Brennen wird er dann automatisch benutzt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== ToDo ==&lt;br /&gt;
&lt;br /&gt;
=== Support großer ATMegas ===&lt;br /&gt;
&lt;br /&gt;
Peter und andere arbeiten momentan noch an der Anpassung für größere Controller wie den ATMega 128.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komfortables Stand-Alone Firmware-Update ===&lt;br /&gt;
&lt;br /&gt;
Basierend auf dem Code der IDE wird es im September noch ein Standalone Programm für das Firmware-Update geben. Es wird auch ein entsprechendes Protokoll zur Versionskontrollle haben. (bisherige Soft- und viel wichtiger Hardwareversion wird gechecked). Eine Verschlüsselung der Firmware-Datei ist in Planung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Automatisches erzeugen des Booloaders ===&lt;br /&gt;
Sobald ich Zeit habe kommt in die IDE auch die Möglichkeit, die Anpassungen des Bootloaders von der IDE durchführen zu lassen.&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28209</id>
		<title>AVR Bootloader FastBoot von Peter Dannegger</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_FastBoot_von_Peter_Dannegger&amp;diff=28209"/>
		<updated>2008-05-26T18:23:57Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Was ist ein Bootloader? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Karsten Donat&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Was ist ein Bootloader? ==&lt;br /&gt;
&lt;br /&gt;
Der Bootloader ist selbst ein kleines Programm. Es wird beim Start des Controllers zuerst ausgeführt. Damit das PC-Programm für das Firmware-Update sich melden kann, wartet der Bootloader  eine gewisse Zeit (hier 0,33 Sekunden) auf ein Zeichen über die serielle Schnittstelle (UART  RS232, USB). Kommt dies Zeichen wird die neue Firmware gebrannt. Andernfalls wird das eigentliche Programm des Controllers ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dem eigentlichen Anwendungsprogramm geht natürlich der Platz, den der Bootloader benötigt, verloren. Da Peters Bootloader jedoch in ein 512Bytes (256 Worte!) großes Segment passt, stört das nicht weiter.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird der Programmcode des Microcontrollers mit einem ISP-Dongle in den Flash gebrannt. Aus verschiedenen Gründen kann dies jedoch nicht möglich/ gewünscht sein:&lt;br /&gt;
* Speed: Der ISP-Dongle kann langsam sein (z.B. myAVR mit aktuellem AVRDude)&lt;br /&gt;
* PINs: Man braucht die PINs und will ISP abschalten ( Fuses, aber Vorsicht!, danach kann man nur mit dem Bootloader oder einem STK-500 noch an den Flash)&lt;br /&gt;
* Komfort: Man möchte dem Kunden/Nutzer die Möglichkeit geben, eine neue Firmware selbst einzuspielen. In der Regel hat dieser jedoch keinen ISP-Dongle zur Hand. Eine RS232- oder USB-Schnittstelle ist aber oftmals vorhanden.&lt;br /&gt;
* Sicherheit: Man möchte dem Kunden nicht die Firmware in deassemblierbarer Form geben (über geänderten Bootloader kann die Datei verschlüsselt sein).&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/73196 http://www.mikrocontroller.net/topic/73196] - Forum-Beitrag und Bootloader&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725] - Atmel AVR-Studio (Bootloader compilieren)&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR/Bootloader.pdf http://www.KarstenDonat.de/AVR/Bootloader.pdf] - diese Anleitung als PDF&lt;br /&gt;
* [http://www.KarstenDonat.de/AVR http://www.KarstenDonat.de/AVR] - ATMega IDE 2007&lt;br /&gt;
* [http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion (Fuses beim myAVR ändern)&lt;br /&gt;
* [http://www.kreatives-chaos.com/artikel/fastboot17-frontend-python Python Implementierung des bootloaders]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt http://www.mikrocontroller.net/attachment/27570/Bootloaderprotokoll.txt] - Protokollbeschreibung&lt;br /&gt;
&lt;br /&gt;
== Bootloader anpassen ==&lt;br /&gt;
&lt;br /&gt;
=== CPU-Frequenz, Wartezeit - FASTLOAD.H ===&lt;br /&gt;
&lt;br /&gt;
Bei XTAL die benutzte Frequenz des Controllers einstellen (jungfräuliche AVRs haben oft intern 1MHz aktiviert!).&lt;br /&gt;
Im Standard Makefile von WinAVR steht die Frequenz unter F_CPU&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	XTAL		= 3686400	; 8MHz, not critical&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die Wartezeit auf das Firmware-Update beim Booten anzupassen:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ	BootDelay	= XTAL / 3	; 0.33s&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== UART Port/ Pins – M*.ASM ===&lt;br /&gt;
&lt;br /&gt;
Da die verwendeten Pins für die Schnittstelle freiwählbar sind, müssen diese noch eingestellt werden. Für einige Controller liegen bereits Definitionsdateien bei. Für den ATMega 8 heißt die Datei beispielsweise M8.ASM.&lt;br /&gt;
&lt;br /&gt;
STX_PORT: hier den Sende Port angeben. Für den Hardware-UART des M8 wäre das Port D.&lt;br /&gt;
Es muss aber nicht der Hardware-UART sein! (wenn man den aber eh als Schnittstelle nutzt ist es sinnvoll)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ    STX_PORT        = PORTD&lt;br /&gt;
.equ    STX_DDR         = DDRD&lt;br /&gt;
.equ    STX             = PD1&lt;br /&gt;
&lt;br /&gt;
.equ    SRX_PIN         = PIND&lt;br /&gt;
.equ    SRX_PORT        = PORTD&lt;br /&gt;
.equ    SRX             = PD0&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&#039;&#039;Beispiel für Hardware-UART im ATMega 8, 48, 168&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wenn man einen neuen Controller hinzufügen möchte, muss noch die BufferSize angepasst werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Compilieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Da der Bootloader in Assembler geschrieben ist, kann WinAVR nicht so ohne weiteres damit umgehen.&lt;br /&gt;
Das einfachste ist, sich das AVRStudio von Atmel herunterzuladen [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725]&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis AvrAssembler2 befindet sich der benötigte Assembler. Das Einfachste ist, sich die unter Appnotes benötigte Include Datei des jeweiligen Controllers (oder das komplette Verzeichnis) und die avrasm2.exe ins Verzeichnis des Bootloaders zu kopieren.&lt;br /&gt;
&lt;br /&gt;
Danach wird der Assembler aufgerufen (m8.asm für ATMega 8)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
avrasm2 -fI m8.asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;Bootloader compilieren&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Das AVRStudio lässt sich unter Linux auch mit WINE installieren (die IDE des AVR Studio 4 geht allerdings nicht).&lt;br /&gt;
Der Aufruf des Assemblers ist dann:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wine avrasm2.exe -fI M8.ASM&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;Bootloader mit Hilfe von WINE compilieren&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Brennen des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Der nun erzeugte Bootloader wird mit dem vorhanden ISP Dongle in den AVR gebrannt (Intel Hex Format)&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;avrdude&amp;quot; -p m8 -c avr910 -P com1 -U flash:w:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i&lt;br /&gt;
  -U flash:v:&amp;quot;C:\Developing\AVR\Test\Bootloader Peter Dannegger\fastload_V14\m8 3686400.hex&amp;quot;:i -y&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brennen mit AVRDude auf COM 1 und einem AVR910 kompatiblen Dongle (z.B. myAVR USB)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== Einstellen der Fuses ==&lt;br /&gt;
&lt;br /&gt;
Damit der Bootloader zu Beginn gestartet wird, müssen die entsprechenden Fuses gesetzt werden. Möglich ist dies u.a. mit der AVRDude GUI.&lt;br /&gt;
* BOOTRST muss auf 0 gestellt werden (aktiviert den Bootloader)&lt;br /&gt;
* BOOTSZ muss auf 10 gestellt werden für 256 Worte&lt;br /&gt;
&lt;br /&gt;
Bei einigen Controllern wie z.B. dem ATMega 48 wird das Flag&lt;br /&gt;
* Selfprogramming enabled gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Fuses mit myAVR ändern ===&lt;br /&gt;
&lt;br /&gt;
Der myAVR Dongle unterstützt leider keine Fuses mit AVRDude. Abhilfe liefert hier jedoch das myAVR Worpad Plus. Die Demo-Version (reicht hierfür) kann unter&lt;br /&gt;
&lt;br /&gt;
[http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe http://www.myavr.de/download/myAVR_WorkpadPLUS_Demo.exe] - myAVR Workpad Plus Demoversion&lt;br /&gt;
&lt;br /&gt;
heruntergeladen werden.&lt;br /&gt;
Nach dem Installieren und starten im Menü Extras – Fuse- und Lock-Bits wählen.&lt;br /&gt;
Das Programm ermittelt automatisch den aktuellen Status. Je nach Controller steht der Bootloader Support unter High oder Extended Fuses.&lt;br /&gt;
&lt;br /&gt;
== Brennen des eigentlichen Programmes ==&lt;br /&gt;
&lt;br /&gt;
Damit das Programm mit Peters Tool in den Controller geladen werden kann, muss im Intel HEX Format vorliegen.&lt;br /&gt;
Im makefile Template von WinAVR (und auch der ATMegaIDE) ist dies standardmäßig eingestellt.&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
# Output format. (can be srec, ihex, binary)&lt;br /&gt;
FORMAT = ihex&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach wird Peters Firmware-Update Tool aufgerufen (hier COM 2)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fboot /C2 /Pmain.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;hier wird die main.hex als Hauptprogramm gebrannt&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wichtig hierbei ist, dass der Dateiname der alten DOS Konvention entspricht. Also keine langen Dateinamen.&lt;br /&gt;
&lt;br /&gt;
Wenn noch keine Firmware im Controller ist (direkt nach Installieren des Bootloaders) startet der Brennvorgang automatisch. Andernfalls wartet das Programm auf den Reset des Controllers.&lt;br /&gt;
Peters Tool arbeitet nur mit COM1 bis COM4 zusammen. Also ggf. den USB Adapter im Gerätemanager umstellen.&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe sieht dann wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C:\DOCUME~1\zzzzzz\Desktop\avr\_soft\fboot18&amp;gt;fboot18 /Ptest.hex /b9600 /c1&lt;br /&gt;
COM 1 at 9600 Baud: Connected (One wire)&lt;br /&gt;
Bootloader V1.8&lt;br /&gt;
Target: 1E9108&lt;br /&gt;
Buffer: 32 Byte&lt;br /&gt;
Size available: 1534 Byte&lt;br /&gt;
Program test.hex: 00000 - 00073 successful&lt;br /&gt;
CRC: o.k.&lt;br /&gt;
Elapsed time: 1.43 seconds&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bootloader-Support in ATMegaIDE 2007 ==&lt;br /&gt;
&lt;br /&gt;
Die IDE kann wahlweise mit AVRDude und einem entsprechenden ISP-Dongle oder mit dem Bootloader das Programm in den Flash brennen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anpassen des Programms ===&lt;br /&gt;
Um das Firmware Update komfortabler zu gestalten kann ein Software-Reset eingebaut werden, der per RS232/ USB Schnittstelle oder Tastendruck ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==== Tastendruck ====&lt;br /&gt;
In der mitgelieferten Standard.h gibt es den Befehl void Reset();. Er löst mit Hilfe des Watchdog-Timers einen Hardwarereset aus.&lt;br /&gt;
Man kann jetzt z.B. auf einen bestimmten Tastendruck (-kombination) hin diesen Reset ausführen.&lt;br /&gt;
&lt;br /&gt;
==== RS232/ USB ====&lt;br /&gt;
Alternativ kann ein entsprechender Befehl über die RS232 gesendet werden. Standardmäßig ist dies 0xFF ‚R‘.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Einstellungen in der IDE ===&lt;br /&gt;
&lt;br /&gt;
Der Bootloader Support kann bei den Projekt-Eigenschaften eingestellt werden. Beim Brennen wird er dann automatisch benutzt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
== ToDo ==&lt;br /&gt;
&lt;br /&gt;
=== Support großer ATMegas ===&lt;br /&gt;
&lt;br /&gt;
Peter und andere arbeiten momentan noch an der Anpassung für größere Controller wie den ATMega 128.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komfortables Stand-Alone Firmware-Update ===&lt;br /&gt;
&lt;br /&gt;
Basierend auf dem Code der IDE wird es im September noch ein Standalone Programm für das Firmware-Update geben. Es wird auch ein entsprechendes Protokoll zur Versionskontrollle haben. (bisherige Soft- und viel wichtiger Hardwareversion wird gechecked). Eine Verschlüsselung der Firmware-Datei ist in Planung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Automatisches erzeugen des Booloaders ===&lt;br /&gt;
Sobald ich Zeit habe kommt in die IDE auch die Möglichkeit, die Anpassungen des Bootloaders von der IDE durchführen zu lassen.&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=LCD&amp;diff=28192</id>
		<title>LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=LCD&amp;diff=28192"/>
		<updated>2008-05-26T13:12:21Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Aufbau */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Aufbau ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;L&#039;&#039;&#039;iquid &#039;&#039;&#039;C&#039;&#039;&#039;rystal &#039;&#039;&#039;D&#039;&#039;&#039;isplays, kurz LCDs, bestehen aus einer großen Fläche von Flüssigkristallen, die unter Einwirkung eines elektrischen Feldes die Polarisationsebene des Lichts drehen. Wird ein solches Display von bereits polarisiertem Licht durchleuchtet, dann erscheinen diese Kristalle mehr oder weniger hell oder dunkel. Die angelegte Spannung ist sehr gering, es muss sich aber um eine Wechselspannung handeln, da eine Gleichspannung die Kristalle zersetzt.&lt;br /&gt;
&lt;br /&gt;
=== Beleuchtung ===&lt;br /&gt;
&lt;br /&gt;
Die Beleuchtung eines LCDs kann durch verschiedenfarbige LEDs erfolgen. Auch floureszierende Materialien können zur Erhellung verwendet werden. Früher wurden CCFL Folien (Leuchtröhren-Technik) und somit Hochspannung dafür genutzt.&lt;br /&gt;
&lt;br /&gt;
=== Schnittstellen ===&lt;br /&gt;
Die gängigen Schnittstellen zu einem LCD-Controller sind ein 8bit, 4bit oder serielles Interface.&lt;br /&gt;
&lt;br /&gt;
Bei vielen &amp;quot;8bit&amp;quot;-LCDs ist es möglich, bei der Konfiguration festzulegen, dass auf 4bit Datenleitungen jeweils 2 nibble hintereinander gesendet werden. Dies spart Anschlüsse auf der Datensteuterungseinheit (PC, µC, etc). Bei 4-Bit-Ansteuerung darauf achten, dass die Datenpins 4-7 und nicht die Datenpins 0-3 des Displays an den µC angeschlossen werden müssen!&lt;br /&gt;
&lt;br /&gt;
=== Touchscreen ===&lt;br /&gt;
&lt;br /&gt;
Ein LCD kann auch mit &#039;&#039;Touchscreen&#039;&#039;- oder &#039;&#039;Touch Panel&#039;&#039;-Funktion hergestellt werden, um eine Eingabe zu ermöglichen. Dazu wird das Display mit einer berührempfindlichen, aber transparenten Oberfläche hergestellt. Ein Selbstbauvorschlag und Links zu einigen kommerziellen Lösungen findet sich [http://www.mikrocontroller.net/forum/read-3-34005 hier].&lt;br /&gt;
&lt;br /&gt;
=== Hacks ===&lt;br /&gt;
&lt;br /&gt;
Eine bei Handybastlern bekannte Spielerei ist das Abziehen der Polarisationsfolie vom LCD und dem um 90° gedrehten Aufkleben der alten oder einer neuen Folie. Dies führt zu einem hardwaremäßigen Invertieren der LCD Anzeige.&lt;br /&gt;
&lt;br /&gt;
=== Handy LCDs ===&lt;br /&gt;
&lt;br /&gt;
Es gibt noch eine bei Bastlern vergleichsweise beliebte Alternative zu den &amp;quot;normal&amp;quot; gekauften LCDs. Hier werden Ersatz-LCDs bestimmter Handytypen hergenommen und per µC angesteuert. Dadurch hat man einige Vorteile: Die Controller sind oft (eigentlich immer) bereits integriert, die Größe des gesamten Systems ist meist minimal und die LCDs sind teilweise unverschämt günstig im Vergleich zu KSS0066er oder HD44780er LCDs. Als Beispiel: Ein 132x176 Pixel LCD mit 60k Farben (!) für das Siemens S65 Handy kostet inzwischen bei Ebay um die 10 Euro zzgl. Versand und die benötigte Ansteuerung dazu hat einen Materialwert von maximal nochmal 10 Euro. Das kann man mit gekauften LCDs oder TFTs bei Reichelt &amp;amp; Co. nicht mehr vergleichen. Allerdings erkauft man sich auch ein paar Nachteile: Es gibt teilweise (noch) keine sauberen Companionboards. Das resultiert vor Allem dann in Arbeit, wenn die LCDs mehrere Spannungen zum Laufen benötigen (oft +10V und +3,3V) oder spezielle Anschlüsse von Nöten sind (SMD-Adapter bspw.) und man somit um eine eigens entwickelte Platine schwer herumkommt. Allerdings beschäftigen sich viele Leute damit, von daher wird man oft etwas kopieren können. Als eine Auswahl an beliebten Handy-LCDs hier eine Auflistung von Displays, mit denen oft gearbeitet wird (daher findet man hier auch massenhaft Infos im Forum).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nokia 3310&#039;&#039;&#039; &amp;amp; kompatible LCD: S/W mit 48x84 Pixeln und per SPI ansteuerbar. Hier gibt es massig Software und Hardware. Im Forum einfach mal suchen oder sich hier umschauen: [http://serdisplib.sourceforge.net/ser/pcd8544.html serdisp3310] Auch avrfreaks und weitere Bastler beschäfitgen sich sehr viel mit diesem sehr günstigen Display (durchschnittlich 5€ bei Ebay). [http://www.mikrocontroller.net/topic/77846#648037 Hier] bspw. gibts ein passendes PCB dazu.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nokia 6100&#039;&#039;&#039; &amp;amp; kompatible: 132x132 Pixel mit 4096 Farben und ebenfalls SPI. Ein sehr beliebtes Display, mit dem sich ebenfalls sehr viele beschäftigen. Einziges Hauptproblem: Es gibt zwei unterschiedliche Controller ([http://www.e-dsp.com/controlling-a-color-graphic-lcd-epson-s1d15g10-controller-with-an-atmel-avr-atmega32l/ Epson S1D15G10] und [http://thomaspfeifer.net/nokia_6100_display.htm Philips PCF8833]) für dasselbe Display, die allerdings nicht kompatibel angesteuert werden! Von daher ist Vorsicht geboten. Zu kaufen gibt es die Displays ab etwa 15€. Man benötigt unter Umständen noch einen SMD-Adapter, den es für etwa 4€ gibt (je nach gekaufter Anzahl). Infos gibts hier: [http://serdisplib.sourceforge.net/ser/nokcol_15g10.html serdisp6100] [http://www.sparkfun.com/commerce/product_info.php?products_id=569 sparkfun] [http://thomaspfeifer.net/nokia_6100_display.htm ThomasPfeifer] und (wie sollte es anders sein) natürlich direkt vor Ort: [http://www.mikrocontroller.net/topic/12218#new Forumsbeitrag]&lt;br /&gt;
&lt;br /&gt;
UPDATE 17.10.2007: Neuerdings ist ein zusätzlicher (und damit dritter) Controllerchip recht beliebt geworden: Epson S1D15G17 (also G17 statt G10). Das Tolle hierbei: Dieser ist zu keinem der anderen Controller voll kompatibel, sondern teilt sich nur einige Befehle. Und zudem gibts bisher noch sehr wenig Infos zu diesem Controller. Also noch mehr aufpassen beim Kauf bzw. dem Ansteuern!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Siemens S65&#039;&#039;&#039; &amp;amp; kompatible: 132x176 Pixel mit 60k Farben und wiederum SPI. Ein vergleichsweise neues LCD, das erst seit etwa ein/zwei Jahren erfolgreich angesteuert wird, obwohl man nicht einmal den verbauten Controller kennt! Re-Engineering sei Dank! Dieses Display gibt es momentan noch unverschämt günstig für etwa 10€, nur die benötigten Spannungen machen Kopfweh (10V für die HG-Beleuchtung und 2,8V für den LCD-Controller). Das Display ist wirklich vergleichsweise groß (das Größte aller hier vorgestellten) und auch noch aus einer größeren Entfernung wunderbar zu lesen/sehen. Einziges Problem: Da pro Pixel zwei volle Bytes gesendet werden müssen, benötigt ein komplett zu beschriftender Bildschirm mit 8 MHz SPI etwa 50 ms für einen Refresh. Das sieht man bspw. bei einem Übergang von schwarz auf weiß schon relativ deutlich.&lt;br /&gt;
Infos gibt es momentan noch relativ sporadisch, aber natürlich wieder hier: [http://www.mikrocontroller.net/topic/31403#new Forumsbeitrag] [http://www.superkranz.de/christian/S65_Display/DisplayIndex.html ChristianKranz]&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss eine Anmerkung: Falls man noch keinen LCD angesteuert hat, bloß nicht mit Handy-LCDs anfangen! Man sollte hier schon einige Erfahrung mitbringen und die kleinen Probleme des Alltags selbst aus dem Weg räumen können. PCB-Design und ein gutes Verständnis der SPI-Schnittstelle sollten ebenfalls vorhanden sein, dann kriegt man das auch gebacken.&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung ==&lt;br /&gt;
Direkt hinter der Dot-Matrix sitzen nur ein paar Treiber, die auch die Leitungen bündeln. Das Interface daran bietet keine &amp;quot;Intelligenz&amp;quot;, es werden Signale für Vertical Sync / Horizontal Sync (also Spalten/Zeilenumbruch) sowie ein Clock Signal benötigt. Es werden nacheinander alle Pixel gescant und über eine Signalleitung an/aus mitgeteilt. Da deshalb ein konstanter (hoher) Datenfluss und ein nicht geringer Speicher (Framebuffer) benötigt wird, ist es für Mikrocontroller eher weniger geeignet. &lt;br /&gt;
&lt;br /&gt;
Zur Ansteuerung von LCDs gibt es spezielle LCD Controller, fertige Module mit Controller und einige Prozessoren mit integrierter Ansteuerung (z.B. aus der [[MSP430]]-Serie, oder [[AVR]] wie der ATmega169 ). LCD-Module mit eingebautem Controller lassen sich meist einfach mit einem [[Mikrocontroller]] ansteuern. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Text (character) LCDs&#039;&#039;&#039; verwenden meistens den &#039;&#039;&#039;[[HD44780]]&#039;&#039;&#039; oder einen kompatiblen Controller (z.B [[KS0066]]). Das [[KS0066]] Timing und die Init-Sequenz weichen stark vom Timing des HD44780 ab! Es gibt gute Application Notes zu Displays (mit KS0066) von Hitachi. 1x16 LCDs werden oft wie 2x8 betrieben! D.h. es ist ein &#039;Zeilenwechsel&#039; an Position (z.B. 0x40) nötig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Grafik (graphic) LCDs&#039;&#039;&#039; verwenden z.B. den [[T6963]], den [[SED1330]] oder den [[KS0108]] Controller.&lt;br /&gt;
&lt;br /&gt;
Bei LCDs ohne eigenen Controller (Laptop-LCDs z.B.) ist die direkte Ansteuerung sehr schwierig (weil zeitkritisch), allerdings lässt sich in manchen Fällen ein Standard-Controller nachrüsten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Ansteuerung des Displays gemeistert wurde, muss als nächstes die höheren Grafikfunktionen (Pixel an/aus, Linie, Rechteck, Kreis,...) gemeistert werden. Die entsprechenden Funktionen zum [[Rastern]] sind für qC recht resourcenfressend. Auch das abspeichern kompletter Bitmaps im ROM ist bei größeren Displays nicht wirklich praktikabel so das die Anbindung an ein größeren Speicher meistens Not tut.&lt;br /&gt;
&lt;br /&gt;
== FAQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wie kann ich was auf ein LCD ausgeben?&#039;&#039;&#039; &amp;lt;br&amp;gt; &lt;br /&gt;
Gute Frage! Eine Methode ist sich als Einzelkämpfer durchzubeißen. Dabei helfen Datenblätter und Handbücher sowie die Suche nach Tutorials, Forenbeiträgen und ähnlichen Projekten. Oder man fragt in einem Forum. Dabei steigen die Erfolgsaussichten auf eine hilfreiche Antwort enorm, wenn man seine Hausaufgaben gemacht hat. Also Links zu Datenblättern angeben! Schaltplan beilegen! Vorhandenen Sourcecode beilegen! ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wie kann ich Zahlen auf LCD/UART ausgeben?&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Siehe die Artikel zur allgemeinen [[FAQ#Wie_kann_ich_Zahlen_auf_LCD.2FUART_ausgeben.3F| FAQ]] und zur [[Festkommaarithmetik]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Egal was ich mache auf dem Display erscheinen keine Zeichen! Was ist los?&#039;&#039;&#039; &amp;lt;br&amp;gt; &lt;br /&gt;
Möglicherweise ist einfach nur der Kontrast unpassend eingestellt. Es gibt auch LCDs z.B. POWERTIP PC1602LRM-LSO-C von Pollin, die eine negative Kontrastspannung benötigen ([http://www.mikrocontroller.net/topic/81596#681507 Forenbeitrag von Thorsten]); näheres kann das Datenblatt klären. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;R/W Leitung vom Text-LCD ist fix mit GND verbunden und die Ansteuerung mit der LCD/AVR-Library von Peter Fleury funktioniert nicht! Was ist los?&#039;&#039;&#039; &amp;lt;br&amp;gt; &lt;br /&gt;
Die Fleury Library erwartet, dass R/W nicht fix ist, sondern vom Programm gesteuert werden kann. Das muss auch so sein, denn Peter liest das Busy Flag aus, um Warteschleifen zu vermeiden.&lt;br /&gt;
&lt;br /&gt;
== Projekte ==&lt;br /&gt;
=== Projekte mit Text-LCD ===&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: LCD]]&lt;br /&gt;
* [[Projekt LCD an Parallelport]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79609#664268 (A) KS0066U oder Ähnliche --- LCD Treiber]&lt;br /&gt;
&lt;br /&gt;
=== Projekte mit Grafik-LCD ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-160854.html LCD Controller für 640x480 LCD mit mega8515] von Benedikt.&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-422387.html Fontgenerator] zum Erstellen eigener Schriftarten (LCD) von Hauke Radtki. ([[Java]], [[T6963]]c)&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-306961.html LCD Library T6963c] angepasst auf [[AVR-GCC]] von Nico Sachs und Florian.&lt;br /&gt;
* [[Projekt T6963-LCD-Ansteuerung]]&lt;br /&gt;
* [http://thomaspfeifer.net/nokia_6100_display.htm Ansteuerung eines Nokia LCDs mit einem AVR-Controller]  von Thomas Pfeifer&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79738#665417 Digitaler Bilderrahmen mit mega8 S65 Display und SD-Karte] von Matthias Bode&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/81793#683135 Pollin E0855-2 SED1530-Treiber] von Mark Meise (AVR-GCC)&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.geocities.com/dinceraydin/lcd/index.html LCD Info] von Dincer Aydin (Englisch).&lt;br /&gt;
* [http://www.geocities.com/dinceraydin/djlcdsim/djlcdsim.html Dincer&#039;s Text LCD Simulator V 1.021]  (Online, Javascript erforderlich).&lt;br /&gt;
* [http://www.geocities.com/dinceraydin/djgfxlcdsim/djgfxlcdsim.html Dincer&#039;s Graphic LCD Simulator V 1.01] (Online, Javascript erforderlich).&lt;br /&gt;
* [http://www.pacificdisplay.com/lcd_ics.htm Übersicht LCD-Controller und Treiber-ICs] bei Pacific Display Devices. (Englisch)&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
* [http://www.tianma.com/controller.php Übersicht LCD-Controller] bei Tianma. (Englisch)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
* [http://www.sprut.de/electronic/lcd/index.htm Dot-Matrix LCD&#039;s] von Jörg &#039;&#039;sprut&#039;&#039; Bredendiek &lt;br /&gt;
* [http://www.lcd-module.de/deu/knowhow/knowhow.htm LCD - Know How] von Electronic Assembly&lt;br /&gt;
* [http://passworld.co.jp/ForumMSP430/viewforum.php?f=2 Graphic LCDs for Microcontroller] (PassWorld YK, Englisch - Japanisch)&lt;br /&gt;
* [http://www.geocities.com/p9019/lcd.html LCD Pinouts] by Scott Johnson&lt;br /&gt;
* [http://www.myke.com/lcd.htm LCD Interfacing Reference Page] (44780)&lt;br /&gt;
* [http://techref.massmind.org/techref/microchip/pwmvee.htm Vee (negative power supply) (for LCD etc...) from PIC PWM] - Vorschlag zum Erzeugen der geringen, negativen Kontrastspannung bei manchen LCD per µC/PWM und Ladungspumpe.&lt;br /&gt;
* [http://www.edn.com/article/CA6505569.html?spacedesc=designideas&amp;amp;industryid=44217 Microcontroller drives LCD with just one wire] - EDN Design Idea von Noureddine Benabadji, 12/3/07 (PIC10F)&lt;br /&gt;
* [http://www.elvand.com/en/index.php?option=com_content&amp;amp;task=view&amp;amp;id=15&amp;amp;Itemid=40 LCD Font Generator (LFG)] von elvand.com (Freeware)&lt;br /&gt;
* [http://homepages.tesco.net/~steve.lawther/steve/t6963c.htm Toshiba  T6963C Controller based Displays]&lt;br /&gt;
* [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=61156 Using the KS0713/S6B1713/ST7565 LCD driver chips] - Tutorial bei www.avrfreaks.net (kostenkose Registrierung erforderlich)&lt;br /&gt;
* [http://fluessigkristalle.com/index.htm Flüssigkristalle und Flüssigkristallanzeigen] - Infos für Lehrende und Lernende inkl. Selbstbauanleitungen von Dr. Feodor Oestreicher.&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diode&amp;diff=28115</id>
		<title>Diode</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diode&amp;diff=28115"/>
		<updated>2008-05-24T00:24:59Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Z-Diode */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Die Bezeichung Diode wurde ursprünglich für Elektronenröhren mit zwei Elektroden verwendet, später für das Halbleiter-Bauteil mit dem selben elektrischen Verhalten. &amp;quot;Di&amp;quot; wegen &#039;&#039;&#039;zwei&#039;&#039;&#039; und &amp;quot;ode&amp;quot; vermutlich wegen Elektr&#039;&#039;&#039;ode&#039;&#039;&#039;, auch wenn bei Wikipedia was anderes steht (Es gibt bei den Elektronenröhren z.B. auch &#039;&#039;Tri&#039;&#039;oden mit drei oder &#039;&#039;Pent&#039;&#039;oden mit fünf Elektroden).&lt;br /&gt;
&lt;br /&gt;
Eine Diode sperrt den Stromfluss in einer Richtung und erlaubt ihn in der anderen. Als diskretes Bauteil besitzen (Halbleiter-) Dioden i.d.R. einen aufgedruckten Ring, der die Kathode kennzeichnet. Liegt an diesem Anschluss  eine negative Spannung, ist die Diode in Durchlassrichtung geschaltet, d.h. sobald die [[Durchlass-Spannung]] überschritten wird, setzt der Stromfluss ein. Bei der umgekehrten Polarität &amp;quot;sperrt&amp;quot; die Diode den Stromfluss. &lt;br /&gt;
&lt;br /&gt;
Beim Betrieb von Dioden sind insbesondere drei wichtige Kenngrößen zu beachten:&lt;br /&gt;
* max. Durchlassstrom (temperaturabhängig) in Durchlassrichtung,&lt;br /&gt;
* max. Sperrspannung in Sperrrichtung und &lt;br /&gt;
* größte zulässige Verlustleistung aufgrund des Spannungsabfalls in Durchlassrichtung.&lt;br /&gt;
&lt;br /&gt;
Das Überschreiten der zulässigen Grenzwerte führt zur Überlastung der Diode.&lt;br /&gt;
&lt;br /&gt;
Dioden werden in elektronischen Schaltungen sehr häufig und zu ganz unterschiedlichen Zwecken eingesetzt, z.B.&lt;br /&gt;
* zur Gleichrichtung,&lt;br /&gt;
* als [[Spannungsreferenz]] (siehe auch Z-Diode),&lt;br /&gt;
* als elektrisch veränderliche Kapazität (Varaktor), oder&lt;br /&gt;
* zum Schutz gegen falsche Polung und Überspannung (Beispiel: [[Relais_mit_Logik_ansteuern#Freilaufdiode|Freilaufdiode]]).&lt;br /&gt;
&lt;br /&gt;
Dioden mit der letzteren Funktion sind heute auch oft in ICs wie [[Mikrocontroller]]n integriert und helfen, die Ein- und Ausgangs-Pins dieser teilweise sehr teuren Bauelemente vor moderater Fehlbehandlung zu schützen.&lt;br /&gt;
&lt;br /&gt;
Eine Sonderform der Dioden sind [[LED]]s, die elektrisch zwar auch Diodenverhalten besitzen, deren eigentlicher Zweck aber die Erzeugung von sichtbarem oder infrarotem Licht ist.&lt;br /&gt;
&lt;br /&gt;
== Z-Diode ==&lt;br /&gt;
&lt;br /&gt;
(auch &#039;&#039;Zener-Diode&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Spezielle Sorte einer Halbleiter-[[Diode]], die unbeschadet einen bestimmten Strom in Sperrichtung nach Überschreiten der Durchbruchsspannung aushält.&lt;br /&gt;
&lt;br /&gt;
Halbleiter haben die Eigenschaft, einen Stromfluss ab einer bestimmten Schwellspannung zu ermöglichen. Dies ist auch bei Z-Dioden der Fall. Sie sind wie normale Siliziumdioden verwendbar, können aber nicht so hohe negative Spannungen sperren (Durchbruch- oder Sperrspannung).&lt;br /&gt;
&lt;br /&gt;
Dieser Effekt wird Zener-Effekt genannt und ist bis ca. 5 V dominierend. Darüber überwiegt der Lawineneffekt. Diese Effekte treten auch bei Leuchtdioden und Diodenstrecken in Transistoren auf, sind dort aber nicht der normale Arbeitspunkt (Zerstörung der Bauteile).&lt;br /&gt;
&lt;br /&gt;
Die Durchbruchspannung - auch Z-Spannung genannt - kann zur Spannungsstabilisierung verwendet werden und ist bei diesem Diodentyp der normale Arbeitspunkt.&lt;br /&gt;
&lt;br /&gt;
Als [[Spannungsreferenz]] sind Band-Gap Referenzen einer Z-Diode wegen ihrer Temperatur-Drift von 20mV/K vorzuziehen.&lt;br /&gt;
&lt;br /&gt;
Z-Dioden werden neben der Anwendung in Spannungs-Stabilisierungs-Schaltungen auch gerne als Spannungsbegrenzer eingesetzt. Beispiel: RS232-Pegel =&amp;gt; TTL-Pegel (+-15V =&amp;gt; 5.6V/-0.6V), das sind zwar die Maximalwerte von typischen Logik-Eingängen, funktioniert aber meist tadellos. Z-Dioden müssen immer mit einer Strombegrenzung betrieben werden, praktisch ist das meist ein Vorwiderstand.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Dioden-Übersicht]]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Diode Diode] bei Wikipedia&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Parallax_Propeller&amp;diff=28114</id>
		<title>Parallax Propeller</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Parallax_Propeller&amp;diff=28114"/>
		<updated>2008-05-24T00:08:26Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Der &#039;&#039;&#039;Propeller&#039;&#039;&#039; von Parallax ist ein Mikrocontroller, der mehrere (8) 32-Bit Prozessorkerne enthält. Die Idee dahinter ist, dass statt spezialisierten IO-Komponenten (Timer, PWM, UART) einer der Kerne diese Aufgabe übernehmen kann, was mehr Flexibilität erlaubt, da man nicht auf die Funktionen angewiesen ist, die der Hersteller eingebaut hat. Die Kerne kommunizieren über einen gemeinsamen Speicherbereich, auf den von allen Kernen reihum zugegriffen werden darf.&lt;br /&gt;
&lt;br /&gt;
Der Propeller besitzt einen internen Oszillator, kann jedoch auch extern mit Takt versorgt werden. Beide Taktquellen lassen sich durch die interne PLL vervielfachen. Somit kann der Chip mit bis zu 80 MHz (5 MHz * 16-fach PLL) stabil betrieben werden. Ein Übertakten des Chips ist - in Grenzen - möglich. Ein nettes Feature ist die Möglichkeit, die PLL mitten im Betrieb zu verändern. Dadurch steht bei Bedarf viel Rechenleistung zur Verfügung, in Ruhephasen dagegen kann der Stromverbauch optimiert werden.&lt;br /&gt;
&lt;br /&gt;
Programmieren kann man den Propeller in Assembler und mit der Sprache &#039;&#039;&#039;Spin&#039;&#039;&#039;, die von einem im ROM des Controllers enthaltenen Interpreter ausgeführt wird. Im internen ROM sind noch weitere Daten, wie z.B. Fonts, 16-Bit Sinus- und Log-Tabellen enthalten. Der Sourcecode für den Spin-Intepreter ist [http://forums.parallax.com/forums/default.aspx?f=25&amp;amp;m=252691 hier] frei verfügbar. Eine kostenlose IDE (&amp;quot;Propeller Tool&amp;quot;) kann von der Parallax-Webseite runtergeladen werden, sie ist sowohl für Assembler wie auch für Spin geeignet. &lt;br /&gt;
&lt;br /&gt;
Ein (kommerzieller) C-Compiler mit IDE ist ebenfalls in Arbeit, für Tester gibt es zur Zeit eine Alpha-Version zum Download, die bereits das LMM (Large-Memory-Model) unterstützt.&lt;br /&gt;
&lt;br /&gt;
[[Category:Mikrocontroller]]&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/search?query=%2Bparallax+%2Bpropeller Beiträge im Forum]&lt;br /&gt;
&lt;br /&gt;
* [http://cool-robotix.de/phpbb3 neu gegründetes deutschsprachiger Parallax Propeller-Forum (de)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.propellerforum.de.vu/ Parallax Propeller-Forum (de)]&lt;br /&gt;
&lt;br /&gt;
* [http://forums.parallax.com/forums/default.aspx?f=25 Support-Forum von Parallax (en)]&lt;br /&gt;
&lt;br /&gt;
* [http://parallax.com/tabid/442/Default.aspx Propeller-Downloads (en)]&lt;br /&gt;
&lt;br /&gt;
* [http://obex.parallax.com/ Propeller Object Exchange (en)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.microcontroller-starterkits.de/propeller.html Propeller-Board von Andreas Jakob (de)]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Uhr&amp;diff=28100</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=28100"/>
		<updated>2008-05-23T01:17:31Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Der CTC Modus des Timers */&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 gearbeitet 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;
Eine weitere Besonderheit ist das Register &#039;&#039;&#039;flag&#039;&#039;&#039; (=r19). Dieses Register fungiert als Anzeiger, wie eine Flagge, daher auch der Name. In der ISR wird dieses Register auf 1 gesetzt. Die Hauptschleife, also alles zwischen &#039;&#039;loop&#039;&#039; und &#039;&#039;rjmp loop&#039;&#039;, prüft dieses Flag und nur dann, wenn das Flag auf 1 steht, wird die LCD Ausgabe gemacht und das Flag wieder auf 0 zurückgesetzt. Auf diese Art wird nur dann Rechenzeit für die LCD Ausgabe verbraucht, wenn dies tatsächlich notwendig ist. Die ISR teilt mit dem Flag der Hauptschleife mit, dass eine bestimmte Aufgabe, nämlich der Update der Anzeige gemacht werden muss und die Hauptschleife reagiert darauf bei nächster Gelegenheit, indem sie diese Aufgabe ausführt und setzt das Flag zurück. Solche Flags werden daher auch &#039;&#039;&#039;Job-Flags&#039;&#039;&#039; genannt, weil durch ihr setzten das Abarbeiten eines Jobs (einer Aufgabe) angestoßen wird. Auch hier gilt wieder: Im Grunde würde man in diesem speziellen Beispiel kein Job-Flag benötigen, weil es in der Hauptschleife nur einen einzigen möglichen Job, die Neuausgabe der Uhrzeit, gibt. Sobald aber Programme komplizierter werden und mehrere Jobs möglich sind, sind Job-Flags eine gute Möglichkeit, ein Programm so zu organsieren, dass bestimmte Dinge nur dann gemacht werden, wenn sie notwendig sind.&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 sie 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, dass 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 heißt, 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 denn 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, heißt 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üsste 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, dass bei einem Systemtakt von 4 MHz von einem Interrupt zum nächsten genau 1/100 Sekunde vergeht, denn 40000 / 4000000 = 0.01. 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 und 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ößenordnung. 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>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Uhr&amp;diff=28099</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=28099"/>
		<updated>2008-05-23T01:09:30Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Ganggenauigkeit */&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 gearbeitet 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;
Eine weitere Besonderheit ist das Register &#039;&#039;&#039;flag&#039;&#039;&#039; (=r19). Dieses Register fungiert als Anzeiger, wie eine Flagge, daher auch der Name. In der ISR wird dieses Register auf 1 gesetzt. Die Hauptschleife, also alles zwischen &#039;&#039;loop&#039;&#039; und &#039;&#039;rjmp loop&#039;&#039;, prüft dieses Flag und nur dann, wenn das Flag auf 1 steht, wird die LCD Ausgabe gemacht und das Flag wieder auf 0 zurückgesetzt. Auf diese Art wird nur dann Rechenzeit für die LCD Ausgabe verbraucht, wenn dies tatsächlich notwendig ist. Die ISR teilt mit dem Flag der Hauptschleife mit, dass eine bestimmte Aufgabe, nämlich der Update der Anzeige gemacht werden muss und die Hauptschleife reagiert darauf bei nächster Gelegenheit, indem sie diese Aufgabe ausführt und setzt das Flag zurück. Solche Flags werden daher auch &#039;&#039;&#039;Job-Flags&#039;&#039;&#039; genannt, weil durch ihr setzten das Abarbeiten eines Jobs (einer Aufgabe) angestoßen wird. Auch hier gilt wieder: Im Grunde würde man in diesem speziellen Beispiel kein Job-Flag benötigen, weil es in der Hauptschleife nur einen einzigen möglichen Job, die Neuausgabe der Uhrzeit, gibt. Sobald aber Programme komplizierter werden und mehrere Jobs möglich sind, sind Job-Flags eine gute Möglichkeit, ein Programm so zu organsieren, dass bestimmte Dinge nur dann gemacht werden, wenn sie notwendig sind.&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 sie 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, dass 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 heißt, 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, denn 40000 / 4000000 = 0.01. 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>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Uhr&amp;diff=28098</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=28098"/>
		<updated>2008-05-23T01:04:41Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Das erste Programm */&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 gearbeitet 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;
Eine weitere Besonderheit ist das Register &#039;&#039;&#039;flag&#039;&#039;&#039; (=r19). Dieses Register fungiert als Anzeiger, wie eine Flagge, daher auch der Name. In der ISR wird dieses Register auf 1 gesetzt. Die Hauptschleife, also alles zwischen &#039;&#039;loop&#039;&#039; und &#039;&#039;rjmp loop&#039;&#039;, prüft dieses Flag und nur dann, wenn das Flag auf 1 steht, wird die LCD Ausgabe gemacht und das Flag wieder auf 0 zurückgesetzt. Auf diese Art wird nur dann Rechenzeit für die LCD Ausgabe verbraucht, wenn dies tatsächlich notwendig ist. Die ISR teilt mit dem Flag der Hauptschleife mit, dass eine bestimmte Aufgabe, nämlich der Update der Anzeige gemacht werden muss und die Hauptschleife reagiert darauf bei nächster Gelegenheit, indem sie diese Aufgabe ausführt und setzt das Flag zurück. Solche Flags werden daher auch &#039;&#039;&#039;Job-Flags&#039;&#039;&#039; genannt, weil durch ihr setzten das Abarbeiten eines Jobs (einer Aufgabe) angestoßen wird. Auch hier gilt wieder: Im Grunde würde man in diesem speziellen Beispiel kein Job-Flag benötigen, weil es in der Hauptschleife nur einen einzigen möglichen Job, die Neuausgabe der Uhrzeit, gibt. Sobald aber Programme komplizierter werden und mehrere Jobs möglich sind, sind Job-Flags eine gute Möglichkeit, ein Programm so zu organsieren, dass bestimmte Dinge nur dann gemacht werden, wenn sie notwendig sind.&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, denn 40000 / 4000000 = 0.01. 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>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=28097</id>
		<title>AVR-Tutorial: Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=28097"/>
		<updated>2008-05-22T23:48:57Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Was geht noch mit einem Timer? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Timer sind eines der Hauptarbeitspferde in unserem Mikrocontroller. Mit ihrer Hilfe ist es möglich, in regelmäßigen Zeitabständen Aktionen zu veranlassen. Aber Timer können noch mehr!&lt;br /&gt;
* Timer können mit einem externen Pin hochgezählt werden&lt;br /&gt;
* Es gibt Möglichkeiten, bei bestimmten Zählerständen einen Interrupt auslösen zu lassen&lt;br /&gt;
* Timer können aber auch völlig selbstständig Signale an einem Ausgabepin erzeugen&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
Aber der Reihe nach.&lt;br /&gt;
&lt;br /&gt;
==Was ist ein Timer?==&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist im Grunde nichts anderes als ein bestimmtes Register im Mikrocontroller, das hardwaregesteuert fortlaufend um 1 erhöht (oder erniedrigt) wird (statt &amp;lt;i&amp;gt;um 1 erhöhen&amp;lt;/i&amp;gt; sagt man auch &#039;&#039;&#039;inkrementieren&#039;&#039;&#039;, und das Gegenstück, &#039;&#039;&#039;dekrementieren&#039;&#039;&#039;, bedeutet &amp;lt;i&amp;gt;um 1 erniedrigen&amp;lt;/i&amp;gt;). Anstatt also Befehle im Programm vorzusehen, die regelmäßig ausgeführt werden und ein Register inkrementieren, erledigt dies der Mikrocontroller ganz von alleine. Dazu ist es möglich, den Timer mit dem Systemtakt zu verbinden und so die Genauigkeit des Quarzes auszunutzen, um ein Register regelmässig und vor allen Dingen unabhängig vom restlichen Programmfluss (!) hochzählen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Davon alleine hätte man aber noch keinen großen Gewinn. Nützlich wird das Ganze erst dann, wenn man bei bestimmten Zählerständen eine Aktion ausführen lassen kann. Einer der &#039;bestimmten Zählerstände&#039; ist zum Beispiel der &#039;&#039;&#039;Overflow&#039;&#039;&#039;. Das Zählregister eines Timers kann natürlich nicht beliebig lange inkrementiert werden – z. B. ist der höchste Zählerstand, den ein 8-Bit-Timer erreichen kann, 2&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt; – 1 = 255. Beim nächsten Inkrementierschritt tritt ein Überlauf (engl. Overflow) auf, der den Timerstand wieder zu 0 werden lässt. Und hier liegt der springende Punkt. Wir können uns nämlich an diesen Overflow &amp;quot;anhängen&amp;quot; und den Controller so konfigurieren, dass beim Auftreten des Timer-Overflows ein Interrupt ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==Der Vorteiler (Prescaler)==&lt;br /&gt;
&lt;br /&gt;
Wenn also der Quarzoszillator mit 4 MHz schwingt, dann würde auch der Timer 4 Millionen mal in der Sekunde erhöht werden. Da der Timer jedes Mal von 0 bis 255 zählt, bevor ein Overflow auftritt, heist das auch, dass in einer Sekunde 4000000 / 256 = 15625 Overflows vorkommen. Ganz schön schnell! Nur: Oft ist das nicht sinnvoll. Um diese Raten zu verzögern, gibt es den Vorteiler, oder auf Englisch, Prescaler. Er kann auf die Werte 1, 8, 64, 256 oder 1024 eingestellt werden. Seine Aufgabe ist es, den Systemtakt um den angegebenen Faktor zu teilen. Steht der Vorteiler also auf 1024, so wird nur bei jedem 1024-ten Impuls vom Systemtakt das Timerregister um 1 erhöht. Entsprechend weniger häufig kommen dann natürlich die Overflows. Der Systemtakt sei wieder 4000000. Dann wird der Timer in 1 Sekunde 4000000 / 1024 = 3906.25 mal erhöht. Da der Timer wieder jedesmal bis 255 zählen muss bis ein Overflow auftritt, bedeutet dies, dass in 1 Sekunde 3906.25 / 256 = 15.25 Overflows auftreten.&lt;br /&gt;
&lt;br /&gt;
Systemtakt: 4Mhz&lt;br /&gt;
&lt;br /&gt;
  Vorteiler    Overflows/Sekunde      Zeit zwischen&lt;br /&gt;
                                      2 Overflows [s]&lt;br /&gt;
&lt;br /&gt;
      1            15625                0.000064&lt;br /&gt;
      8             1953.125            0.000512&lt;br /&gt;
     64              244.1406           0.004096&lt;br /&gt;
    256               61.0351           0.016384&lt;br /&gt;
   1024               15.2587           0.065536&lt;br /&gt;
&lt;br /&gt;
==Erste Tests==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das einen Timer Overflow in Aktion zeigt, könnte z.B. so aussehen:&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 leds = r17&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, 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, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     leds, 0xFF&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, 0b00000001      ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&lt;br /&gt;
        sei&lt;br /&gt;
&lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
timer0_overflow:                      ; Timer 0 Overflow Handler&lt;br /&gt;
        out     PORTB, leds&lt;br /&gt;
        com     leds&lt;br /&gt;
        reti&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm beginnt mit der [[AVR-Tutorial:_Interrupts|Interrupt-Vektoren-Tabelle]]. Dort ist an der Adresse &amp;lt;i&amp;gt;OVF0Addr&amp;lt;/i&amp;gt; ein Sprung zur Marke &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt; eingetragen. Wenn also ein Overflow Interrupt vom Timer 0 auftritt, so wird dieser Interrupt durch den &#039;&#039;&#039;rjmp&#039;&#039;&#039; weitergeleitet an die Stelle &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Hauptprogramm beginnt ganz normal mit der Belegung des Stackpointers. Danach wird der Port B auf Ausgang geschaltet, wir wollen hier wieder die LED anschliessen.&lt;br /&gt;
&lt;br /&gt;
Durch Beschreiben von TCCR0 mit dem Bitmuster 0b00000001 wird der Vorteiler auf 1 gesetzt. Für die ersten Versuche empfiehlt es sich, das Programm mit dem AVR-Studio zunächst zu simulieren. Würden wir einen größeren Vorteiler benutzen, so müsste man ziemlich oft mittels F11 einen simulierten Schritt ausführen, um eine Änderung im Timerregister zu erreichen.&lt;br /&gt;
&lt;br /&gt;
Die nächsten Anweisungen setzen im TIMSK Register das TOIE0 Bit. Sinn der Sache ist es, dem Timer zu erlauben, bei Erreichen eines Overflow einen Interrupt auszulösen.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss noch die Interrupts generell mittels &#039;&#039;&#039;sei&#039;&#039;&#039; freigeben. Dieser Schritt ist obligatorisch. Im Mikrocontroller können viele Quellen einen Interrupt auslösen. Daraus folgt: Für jede mögliche Quelle muss festgelegt werden, ob sie einen Interrupt erzeugen darf oder nicht. Die Oberhoheit hat aber das globale Interrupt Flag. Mit ihm können alle Interrupts, egal von welcher Quelle sie kommen, unterdrückt werden.&lt;br /&gt;
&lt;br /&gt;
Damit ist die Initialisierung beendet und das Hauptprogramm kann sich schlafen legen. Die &#039;&#039;&#039;loop: rjmp loop&#039;&#039;&#039; Schleife macht genau dieses.&lt;br /&gt;
&lt;br /&gt;
Tritt nun ein Overflow am Timer auf, so wird über den Umweg über die Interrupt Vektor Tabelle der Programmteil &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt; angesprungen. Dieser gibt einfach nur den Inhalt des Registers leds am Port B aus. Danach wird das leds Register mit einer &#039;&#039;&#039;com&#039;&#039;&#039; Operation negiert, so dass aus allen 0 Bits eine 1 wird und umgekehrt. Die Overflow Behandlung ist damit beendet und mittels &#039;&#039;&#039;reti&#039;&#039;&#039; wird der Interrupt Handler wieder verlassen.&lt;br /&gt;
&lt;br /&gt;
==Simulation im AVR-Studio==&lt;br /&gt;
&lt;br /&gt;
Es lohnt sich, den ganzen Vorgang im AVR-Studio simulieren zu lassen. Dazu am besten in der linken I/O View Ansicht die Einträge für PORTB und TIMER_COUNTER_0 öffnen. Wird mittels F11 durch das Programm gegangen, so sieht man, dass ab dem Moment, ab dem der Vorteiler auf 1 gesetzt wird, der Timer 0 im TCNT0 Register zu zählen anfängt. Mit jedem Druck auf F11 erhöht sich der Zählerstand. Irgendwann ist dann die Endlosschleife loop erreicht. Drücken Sie weiterhin F11 und beobachten sie, wie TCNT0 immer höher zählt, bis der Overflow erreicht wird. In dem Moment, in dem der Overflow erreicht wird, wird der Interrupt ausgelöst. Mit dem nächsten F11 landen sie in der Interrupt Vektor Tabelle und von dort geht es weiter zu timer_0_overflow. Weitere Tastendrücke von F11 erledigen dann die Ausgabe auf den Port B, das Invertieren des Registers r17 und der Interrupt ist damit behandelt. Nach dem &#039;&#039;&#039;reti&#039;&#039;&#039; macht der Microcontroller genau an der Stelle weiter, an der er vom Interrupt unterbrochen wurde. Und der Timer 0 hat in der Zwischenzeit weitergezählt! Nach exakt weiteren 256 Schritten, vom Auftreten des ersten Overflows an gerechnet, wird der nächste Overflow ausgelöst.&lt;br /&gt;
&lt;br /&gt;
==Wie schnell schaltet denn jetzt der Port?==&lt;br /&gt;
&lt;br /&gt;
Eine berechtigte Frage. Dazu müssen wir etwas rechnen. Keine Angst, es ist nicht schwer, und wer das Prinzip bisher verstanden hat, der sollte keine Schwierigkeiten haben, die Berechnung nachzuvollziehen.&lt;br /&gt;
&lt;br /&gt;
Der Quarzoszillator schwingt mit 4 MHz. Das heißt, in 1 Sekunde werden 4000000 Taktzyklen generiert. Durch die Wahl des Vorteilers von 1 bedeutet das auch, dass der Timer 4000000 mal in der Sekunde erhöht wird. Von einem Overflow zum nächsten muss der Timer 256 Zählvorgänge ausführen. Also werden in 1 Sekunde 4000000 / 256 = 15625 Overflows generiert. Bei jedem Overflow schalten wir die LEDs jeweils in den anderen Zustand. D.h die LEDs blinken mit einer Frequenz von 7812.5 Hz. Das ist zuviel als dass wir es noch sehen könnten.&lt;br /&gt;
&lt;br /&gt;
Was können wir also tun, um diese Blinkfrequenz zu verringern? Im Moment ist unsere einzige Einflussgröße der Vorteiler. Wie sieht die Rechnung aus, wenn wir einen Vorteiler von 1024 wählen?&lt;br /&gt;
&lt;br /&gt;
Wiederrum: Der Systemtakt sei 4 Mhz. Durch den Vorteiler von 1024 werden daraus 4000000 / 1024 = 3906.25 Pulse pro Sekunde für den Timer. Der zählt wiederum 256 Zustände von einem Overflow zum nächsten. 3906.25 / 256 = 15.2587. Und wiederum: Im Overflow werden die LEDs ja abwechselnd ein und ausgeschaltet, also dividieren wir noch durch 2: 15.2587 / 2 = 7.629. Also knapp 7 Hz. Diese Frequenz müsste man schon mit freiem Auge sehen. Die LEDs werden ziemlich schnell vor sich hin blinken.&lt;br /&gt;
&lt;br /&gt;
Reicht diese Verzögerung noch immer nicht, dann haben wir 2 Möglichkeiten:&lt;br /&gt;
* Entweder wir benutzen einen anderen Timer. Timer 1 beispielsweise ist ein 16 Bit Timer. Der Timer zählt also nicht bis 256 sondern bis 65536. Bei entsprechender Umarbeitung des Programms und einem Vorteiler von 1024 bedeutet das, dass die LEDs einen Ein/Aus Zyklus in 33 Sekunden absolvieren.&lt;br /&gt;
* Oder wir schalten die LEDs nicht bei jedem Timer Overflow um. Man könnte zum Beispiel in einem Register bis 7 zählen und nur dann, wenn dieses Register 7 erreicht hat, wird&lt;br /&gt;
** das Register wieder auf 0 gesetzt und&lt;br /&gt;
** die LEDs umgeschaltet.&lt;br /&gt;
&lt;br /&gt;
==Timer 0==&lt;br /&gt;
&lt;br /&gt;
Timer 0 ist ein 8 Bit Timer.&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
&lt;br /&gt;
===TCCR0===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      | CS02 | CS01 | CS00 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS02 - CS00   Clock Select&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;CS02&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;CS01&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;CS00&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;keine (Der Timer ist angehalten)&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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:    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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:    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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:   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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  256&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler: 1024&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Externer Takt vom Pin T0, fallende Flanke&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Externer Takt vom Pin T0, steigende Flanke&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;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      | TOIE0|&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE0         Timer 0 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Ist dieses Bit gesetzt, so wird beim Auftreten eines&lt;br /&gt;
    Overflows am Timer ein Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Anstatt der Schreibweise&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
ist es besser, die Schreibweise&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi     temp, 1 &amp;lt;&amp;lt; TOIE0&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu wählen, da hier unmittelbar aus dem Ladekommando hervorgeht, welche Bedeutung das gesetzte Bit hat. Die vorher inkludierte m8def.inc definiert dazu alles Notwendige.&lt;br /&gt;
&lt;br /&gt;
==Timer 1==&lt;br /&gt;
&lt;br /&gt;
Timer 1 ist ein 16 Bit Timer&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
* Clear Timer on Compare Match&lt;br /&gt;
* Input Capture&lt;br /&gt;
* 2 Compare Einheiten&lt;br /&gt;
* div. PWM Modi&lt;br /&gt;
&lt;br /&gt;
===TCCR1B===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | ICNC1| ICES1|      | WGM13| WGM12| CS12 | CS11 | CS10 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS12 - CS10   Clock Select&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&amp;gt;CS12&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS11&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS10&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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;keine (Der Timer ist angehalten)&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;Vorteiler:    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;&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;Vorteiler:    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;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;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  256&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;Vorteiler: 1024&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;Externer Takt vom Pin T1, fallende Flanke&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;Externer Takt vom Pin T1, steigende Flanke&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;
  &amp;lt;i&amp;gt;ICES1   Input Capture Edge Select&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;ICNC1   Input Capture Noise Canceler&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TCCR1A===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |COM1A1|COM1A0|COM1B1|COM1B0|FOC1A |FOC1B |WGM11 |WGM10 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===OCR1A===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===OCR1B===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===ICR1===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |TICIE1|OCIE1A|OCIE1B| TOIE1|      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TICIE1    Timer 1 Input Capture Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE1A    Timer 1 Output Compare A Match Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE1B    Timer 1 Output Compare B Match Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE1     Timer 1 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Timer 2==&lt;br /&gt;
Timer 2 ist ein 8 Bit Timer.&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
* Compare Match Interrupt&lt;br /&gt;
* Clear Timer on Compare Match&lt;br /&gt;
* Phasen korrekte PWM&lt;br /&gt;
&lt;br /&gt;
===TCCR2===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | FOC2 | WGM20| COM21| COM20| WGM21| CS22 | CS21 | CS20 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS22 - CS20   Clock Select&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&amp;gt;CS22&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS21&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS20&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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;keine (Der Timer ist angehalten)&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;Vorteiler:    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;&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;Vorteiler:    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;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;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  128&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;Vorteiler:  256&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;Vorteiler: 1024&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;
===OCR2===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | OCIE2| TOIE2|      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE2   Timer 2 Output Compare Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE2   Timer 2 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Was geht noch mit einem Timer?==&lt;br /&gt;
&lt;br /&gt;
Timer sind sehr universelle Microcontroller Bestandteile. Für weitergehende Studien ist es daher unerlässlich, das entsprechende Datenblatt des Microcontrollers zu studieren. Oft ist es z.B. möglich, dass der Timer bei erreichen von bestimmten Zählerständen einen Ausgabepin von sich aus ein-/aus-/umschaltet. Er erledigt dann das, was wir oben noch mit einem Interrupt gemacht haben, eigenständig komplett in Hardware. Bei einigen Timern ist es möglich, damit eine [[PWM]] (Pulsweiten-Modulation) aufzubauen.&lt;br /&gt;
&lt;br /&gt;
Ein paar der Timermodule lassen sich auch als Counter verwenden. Damit kann man z.B. die Anzahl externer Ereignisse wie Schaltvorgänge eines Inkrementalgebers bestimmen.&lt;br /&gt;
&lt;br /&gt;
Andere bieten die Möglichkeit, über einen externen Uhrenquarz getaktet zu werden (Anwendung z.B. eine &amp;quot;Echtzeituhr&amp;quot; oder als &amp;quot;Weckfunktion&amp;quot; aus einem&lt;br /&gt;
Standby/Powerdownmodus).&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* Timer/Counter und PWM beim ATMega16 Mikrocontroller: http://www.uni-koblenz.de/~physik/informatik/MCU/Timer.pdf &lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Speicher|&lt;br /&gt;
zurücklink=AVR-Tutorial: Speicher|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Uhr|&lt;br /&gt;
vorlink=AVR-Tutorial: Uhr}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=28096</id>
		<title>AVR-Tutorial: Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=28096"/>
		<updated>2008-05-22T23:44:41Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Wie schnell schaltet denn jetzt der Port? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Timer sind eines der Hauptarbeitspferde in unserem Mikrocontroller. Mit ihrer Hilfe ist es möglich, in regelmäßigen Zeitabständen Aktionen zu veranlassen. Aber Timer können noch mehr!&lt;br /&gt;
* Timer können mit einem externen Pin hochgezählt werden&lt;br /&gt;
* Es gibt Möglichkeiten, bei bestimmten Zählerständen einen Interrupt auslösen zu lassen&lt;br /&gt;
* Timer können aber auch völlig selbstständig Signale an einem Ausgabepin erzeugen&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
Aber der Reihe nach.&lt;br /&gt;
&lt;br /&gt;
==Was ist ein Timer?==&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist im Grunde nichts anderes als ein bestimmtes Register im Mikrocontroller, das hardwaregesteuert fortlaufend um 1 erhöht (oder erniedrigt) wird (statt &amp;lt;i&amp;gt;um 1 erhöhen&amp;lt;/i&amp;gt; sagt man auch &#039;&#039;&#039;inkrementieren&#039;&#039;&#039;, und das Gegenstück, &#039;&#039;&#039;dekrementieren&#039;&#039;&#039;, bedeutet &amp;lt;i&amp;gt;um 1 erniedrigen&amp;lt;/i&amp;gt;). Anstatt also Befehle im Programm vorzusehen, die regelmäßig ausgeführt werden und ein Register inkrementieren, erledigt dies der Mikrocontroller ganz von alleine. Dazu ist es möglich, den Timer mit dem Systemtakt zu verbinden und so die Genauigkeit des Quarzes auszunutzen, um ein Register regelmässig und vor allen Dingen unabhängig vom restlichen Programmfluss (!) hochzählen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Davon alleine hätte man aber noch keinen großen Gewinn. Nützlich wird das Ganze erst dann, wenn man bei bestimmten Zählerständen eine Aktion ausführen lassen kann. Einer der &#039;bestimmten Zählerstände&#039; ist zum Beispiel der &#039;&#039;&#039;Overflow&#039;&#039;&#039;. Das Zählregister eines Timers kann natürlich nicht beliebig lange inkrementiert werden – z. B. ist der höchste Zählerstand, den ein 8-Bit-Timer erreichen kann, 2&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt; – 1 = 255. Beim nächsten Inkrementierschritt tritt ein Überlauf (engl. Overflow) auf, der den Timerstand wieder zu 0 werden lässt. Und hier liegt der springende Punkt. Wir können uns nämlich an diesen Overflow &amp;quot;anhängen&amp;quot; und den Controller so konfigurieren, dass beim Auftreten des Timer-Overflows ein Interrupt ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==Der Vorteiler (Prescaler)==&lt;br /&gt;
&lt;br /&gt;
Wenn also der Quarzoszillator mit 4 MHz schwingt, dann würde auch der Timer 4 Millionen mal in der Sekunde erhöht werden. Da der Timer jedes Mal von 0 bis 255 zählt, bevor ein Overflow auftritt, heist das auch, dass in einer Sekunde 4000000 / 256 = 15625 Overflows vorkommen. Ganz schön schnell! Nur: Oft ist das nicht sinnvoll. Um diese Raten zu verzögern, gibt es den Vorteiler, oder auf Englisch, Prescaler. Er kann auf die Werte 1, 8, 64, 256 oder 1024 eingestellt werden. Seine Aufgabe ist es, den Systemtakt um den angegebenen Faktor zu teilen. Steht der Vorteiler also auf 1024, so wird nur bei jedem 1024-ten Impuls vom Systemtakt das Timerregister um 1 erhöht. Entsprechend weniger häufig kommen dann natürlich die Overflows. Der Systemtakt sei wieder 4000000. Dann wird der Timer in 1 Sekunde 4000000 / 1024 = 3906.25 mal erhöht. Da der Timer wieder jedesmal bis 255 zählen muss bis ein Overflow auftritt, bedeutet dies, dass in 1 Sekunde 3906.25 / 256 = 15.25 Overflows auftreten.&lt;br /&gt;
&lt;br /&gt;
Systemtakt: 4Mhz&lt;br /&gt;
&lt;br /&gt;
  Vorteiler    Overflows/Sekunde      Zeit zwischen&lt;br /&gt;
                                      2 Overflows [s]&lt;br /&gt;
&lt;br /&gt;
      1            15625                0.000064&lt;br /&gt;
      8             1953.125            0.000512&lt;br /&gt;
     64              244.1406           0.004096&lt;br /&gt;
    256               61.0351           0.016384&lt;br /&gt;
   1024               15.2587           0.065536&lt;br /&gt;
&lt;br /&gt;
==Erste Tests==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das einen Timer Overflow in Aktion zeigt, könnte z.B. so aussehen:&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 leds = r17&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, 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, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     leds, 0xFF&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, 0b00000001      ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&lt;br /&gt;
        sei&lt;br /&gt;
&lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
timer0_overflow:                      ; Timer 0 Overflow Handler&lt;br /&gt;
        out     PORTB, leds&lt;br /&gt;
        com     leds&lt;br /&gt;
        reti&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm beginnt mit der [[AVR-Tutorial:_Interrupts|Interrupt-Vektoren-Tabelle]]. Dort ist an der Adresse &amp;lt;i&amp;gt;OVF0Addr&amp;lt;/i&amp;gt; ein Sprung zur Marke &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt; eingetragen. Wenn also ein Overflow Interrupt vom Timer 0 auftritt, so wird dieser Interrupt durch den &#039;&#039;&#039;rjmp&#039;&#039;&#039; weitergeleitet an die Stelle &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Hauptprogramm beginnt ganz normal mit der Belegung des Stackpointers. Danach wird der Port B auf Ausgang geschaltet, wir wollen hier wieder die LED anschliessen.&lt;br /&gt;
&lt;br /&gt;
Durch Beschreiben von TCCR0 mit dem Bitmuster 0b00000001 wird der Vorteiler auf 1 gesetzt. Für die ersten Versuche empfiehlt es sich, das Programm mit dem AVR-Studio zunächst zu simulieren. Würden wir einen größeren Vorteiler benutzen, so müsste man ziemlich oft mittels F11 einen simulierten Schritt ausführen, um eine Änderung im Timerregister zu erreichen.&lt;br /&gt;
&lt;br /&gt;
Die nächsten Anweisungen setzen im TIMSK Register das TOIE0 Bit. Sinn der Sache ist es, dem Timer zu erlauben, bei Erreichen eines Overflow einen Interrupt auszulösen.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss noch die Interrupts generell mittels &#039;&#039;&#039;sei&#039;&#039;&#039; freigeben. Dieser Schritt ist obligatorisch. Im Mikrocontroller können viele Quellen einen Interrupt auslösen. Daraus folgt: Für jede mögliche Quelle muss festgelegt werden, ob sie einen Interrupt erzeugen darf oder nicht. Die Oberhoheit hat aber das globale Interrupt Flag. Mit ihm können alle Interrupts, egal von welcher Quelle sie kommen, unterdrückt werden.&lt;br /&gt;
&lt;br /&gt;
Damit ist die Initialisierung beendet und das Hauptprogramm kann sich schlafen legen. Die &#039;&#039;&#039;loop: rjmp loop&#039;&#039;&#039; Schleife macht genau dieses.&lt;br /&gt;
&lt;br /&gt;
Tritt nun ein Overflow am Timer auf, so wird über den Umweg über die Interrupt Vektor Tabelle der Programmteil &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt; angesprungen. Dieser gibt einfach nur den Inhalt des Registers leds am Port B aus. Danach wird das leds Register mit einer &#039;&#039;&#039;com&#039;&#039;&#039; Operation negiert, so dass aus allen 0 Bits eine 1 wird und umgekehrt. Die Overflow Behandlung ist damit beendet und mittels &#039;&#039;&#039;reti&#039;&#039;&#039; wird der Interrupt Handler wieder verlassen.&lt;br /&gt;
&lt;br /&gt;
==Simulation im AVR-Studio==&lt;br /&gt;
&lt;br /&gt;
Es lohnt sich, den ganzen Vorgang im AVR-Studio simulieren zu lassen. Dazu am besten in der linken I/O View Ansicht die Einträge für PORTB und TIMER_COUNTER_0 öffnen. Wird mittels F11 durch das Programm gegangen, so sieht man, dass ab dem Moment, ab dem der Vorteiler auf 1 gesetzt wird, der Timer 0 im TCNT0 Register zu zählen anfängt. Mit jedem Druck auf F11 erhöht sich der Zählerstand. Irgendwann ist dann die Endlosschleife loop erreicht. Drücken Sie weiterhin F11 und beobachten sie, wie TCNT0 immer höher zählt, bis der Overflow erreicht wird. In dem Moment, in dem der Overflow erreicht wird, wird der Interrupt ausgelöst. Mit dem nächsten F11 landen sie in der Interrupt Vektor Tabelle und von dort geht es weiter zu timer_0_overflow. Weitere Tastendrücke von F11 erledigen dann die Ausgabe auf den Port B, das Invertieren des Registers r17 und der Interrupt ist damit behandelt. Nach dem &#039;&#039;&#039;reti&#039;&#039;&#039; macht der Microcontroller genau an der Stelle weiter, an der er vom Interrupt unterbrochen wurde. Und der Timer 0 hat in der Zwischenzeit weitergezählt! Nach exakt weiteren 256 Schritten, vom Auftreten des ersten Overflows an gerechnet, wird der nächste Overflow ausgelöst.&lt;br /&gt;
&lt;br /&gt;
==Wie schnell schaltet denn jetzt der Port?==&lt;br /&gt;
&lt;br /&gt;
Eine berechtigte Frage. Dazu müssen wir etwas rechnen. Keine Angst, es ist nicht schwer, und wer das Prinzip bisher verstanden hat, der sollte keine Schwierigkeiten haben, die Berechnung nachzuvollziehen.&lt;br /&gt;
&lt;br /&gt;
Der Quarzoszillator schwingt mit 4 MHz. Das heißt, in 1 Sekunde werden 4000000 Taktzyklen generiert. Durch die Wahl des Vorteilers von 1 bedeutet das auch, dass der Timer 4000000 mal in der Sekunde erhöht wird. Von einem Overflow zum nächsten muss der Timer 256 Zählvorgänge ausführen. Also werden in 1 Sekunde 4000000 / 256 = 15625 Overflows generiert. Bei jedem Overflow schalten wir die LEDs jeweils in den anderen Zustand. D.h die LEDs blinken mit einer Frequenz von 7812.5 Hz. Das ist zuviel als dass wir es noch sehen könnten.&lt;br /&gt;
&lt;br /&gt;
Was können wir also tun, um diese Blinkfrequenz zu verringern? Im Moment ist unsere einzige Einflussgröße der Vorteiler. Wie sieht die Rechnung aus, wenn wir einen Vorteiler von 1024 wählen?&lt;br /&gt;
&lt;br /&gt;
Wiederrum: Der Systemtakt sei 4 Mhz. Durch den Vorteiler von 1024 werden daraus 4000000 / 1024 = 3906.25 Pulse pro Sekunde für den Timer. Der zählt wiederum 256 Zustände von einem Overflow zum nächsten. 3906.25 / 256 = 15.2587. Und wiederum: Im Overflow werden die LEDs ja abwechselnd ein und ausgeschaltet, also dividieren wir noch durch 2: 15.2587 / 2 = 7.629. Also knapp 7 Hz. Diese Frequenz müsste man schon mit freiem Auge sehen. Die LEDs werden ziemlich schnell vor sich hin blinken.&lt;br /&gt;
&lt;br /&gt;
Reicht diese Verzögerung noch immer nicht, dann haben wir 2 Möglichkeiten:&lt;br /&gt;
* Entweder wir benutzen einen anderen Timer. Timer 1 beispielsweise ist ein 16 Bit Timer. Der Timer zählt also nicht bis 256 sondern bis 65536. Bei entsprechender Umarbeitung des Programms und einem Vorteiler von 1024 bedeutet das, dass die LEDs einen Ein/Aus Zyklus in 33 Sekunden absolvieren.&lt;br /&gt;
* Oder wir schalten die LEDs nicht bei jedem Timer Overflow um. Man könnte zum Beispiel in einem Register bis 7 zählen und nur dann, wenn dieses Register 7 erreicht hat, wird&lt;br /&gt;
** das Register wieder auf 0 gesetzt und&lt;br /&gt;
** die LEDs umgeschaltet.&lt;br /&gt;
&lt;br /&gt;
==Timer 0==&lt;br /&gt;
&lt;br /&gt;
Timer 0 ist ein 8 Bit Timer.&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
&lt;br /&gt;
===TCCR0===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      | CS02 | CS01 | CS00 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS02 - CS00   Clock Select&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;CS02&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;CS01&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;CS00&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;keine (Der Timer ist angehalten)&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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:    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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:    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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:   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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  256&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler: 1024&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Externer Takt vom Pin T0, fallende Flanke&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Externer Takt vom Pin T0, steigende Flanke&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;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      | TOIE0|&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE0         Timer 0 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Ist dieses Bit gesetzt, so wird beim Auftreten eines&lt;br /&gt;
    Overflows am Timer ein Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Anstatt der Schreibweise&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
ist es besser, die Schreibweise&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi     temp, 1 &amp;lt;&amp;lt; TOIE0&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu wählen, da hier unmittelbar aus dem Ladekommando hervorgeht, welche Bedeutung das gesetzte Bit hat. Die vorher inkludierte m8def.inc definiert dazu alles Notwendige.&lt;br /&gt;
&lt;br /&gt;
==Timer 1==&lt;br /&gt;
&lt;br /&gt;
Timer 1 ist ein 16 Bit Timer&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
* Clear Timer on Compare Match&lt;br /&gt;
* Input Capture&lt;br /&gt;
* 2 Compare Einheiten&lt;br /&gt;
* div. PWM Modi&lt;br /&gt;
&lt;br /&gt;
===TCCR1B===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | ICNC1| ICES1|      | WGM13| WGM12| CS12 | CS11 | CS10 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS12 - CS10   Clock Select&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&amp;gt;CS12&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS11&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS10&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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;keine (Der Timer ist angehalten)&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;Vorteiler:    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;&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;Vorteiler:    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;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;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  256&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;Vorteiler: 1024&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;Externer Takt vom Pin T1, fallende Flanke&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;Externer Takt vom Pin T1, steigende Flanke&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;
  &amp;lt;i&amp;gt;ICES1   Input Capture Edge Select&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;ICNC1   Input Capture Noise Canceler&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TCCR1A===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |COM1A1|COM1A0|COM1B1|COM1B0|FOC1A |FOC1B |WGM11 |WGM10 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===OCR1A===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===OCR1B===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===ICR1===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |TICIE1|OCIE1A|OCIE1B| TOIE1|      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TICIE1    Timer 1 Input Capture Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE1A    Timer 1 Output Compare A Match Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE1B    Timer 1 Output Compare B Match Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE1     Timer 1 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Timer 2==&lt;br /&gt;
Timer 2 ist ein 8 Bit Timer.&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
* Compare Match Interrupt&lt;br /&gt;
* Clear Timer on Compare Match&lt;br /&gt;
* Phasen korrekte PWM&lt;br /&gt;
&lt;br /&gt;
===TCCR2===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | FOC2 | WGM20| COM21| COM20| WGM21| CS22 | CS21 | CS20 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS22 - CS20   Clock Select&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&amp;gt;CS22&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS21&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS20&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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;keine (Der Timer ist angehalten)&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;Vorteiler:    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;&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;Vorteiler:    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;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;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  128&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;Vorteiler:  256&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;Vorteiler: 1024&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;
===OCR2===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | OCIE2| TOIE2|      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE2   Timer 2 Output Compare Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE2   Timer 2 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Was geht noch mit einem Timer?==&lt;br /&gt;
&lt;br /&gt;
Timer sind sehr universelle Microcontroller Bestandteile. Für weitergehende Studien ist es daher unerlässlich das entsprechende Datenblatt des Microcontrollers zu studieren. Oft ist es zb. möglich, dass der Timer bei erreichen von bestimmten Zählerständen einen Ausgabepin von sich aus ein-/aus-/umschaltet. Er erledigt dann das was wir oben noch mit einem Interrupt gemacht haben eigenständig komplett in Hardware. Bei einigen Timern ist es möglich damit eine [[PWM]] (Pulsweiten-Modulation) aufzubauen.&lt;br /&gt;
&lt;br /&gt;
Ein paar der Timermodule lassen sich auch als Counter verwenden. Damit kann man z.B. die Anzahl externer Ereignisse wie Schaltvorgänge eines Inkrementalgebers bestimmen.&lt;br /&gt;
&lt;br /&gt;
Andere bieten die Möglichkeit über einen externen Uhrenquarz getaktet zu werden. (Anwendung z.B. eine &amp;quot;Echtzeituhr&amp;quot; oder als &amp;quot;Weckfunktion&amp;quot; aus einem&lt;br /&gt;
Standby/Powerdownmodus)&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* Timer/Counter und PWM beim ATMega16 Mikrocontroller: http://www.uni-koblenz.de/~physik/informatik/MCU/Timer.pdf &lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Speicher|&lt;br /&gt;
zurücklink=AVR-Tutorial: Speicher|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Uhr|&lt;br /&gt;
vorlink=AVR-Tutorial: Uhr}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=28095</id>
		<title>AVR-Tutorial: Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=28095"/>
		<updated>2008-05-22T23:14:06Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Simulation im AVR-Studio */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Timer sind eines der Hauptarbeitspferde in unserem Mikrocontroller. Mit ihrer Hilfe ist es möglich, in regelmäßigen Zeitabständen Aktionen zu veranlassen. Aber Timer können noch mehr!&lt;br /&gt;
* Timer können mit einem externen Pin hochgezählt werden&lt;br /&gt;
* Es gibt Möglichkeiten, bei bestimmten Zählerständen einen Interrupt auslösen zu lassen&lt;br /&gt;
* Timer können aber auch völlig selbstständig Signale an einem Ausgabepin erzeugen&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
Aber der Reihe nach.&lt;br /&gt;
&lt;br /&gt;
==Was ist ein Timer?==&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist im Grunde nichts anderes als ein bestimmtes Register im Mikrocontroller, das hardwaregesteuert fortlaufend um 1 erhöht (oder erniedrigt) wird (statt &amp;lt;i&amp;gt;um 1 erhöhen&amp;lt;/i&amp;gt; sagt man auch &#039;&#039;&#039;inkrementieren&#039;&#039;&#039;, und das Gegenstück, &#039;&#039;&#039;dekrementieren&#039;&#039;&#039;, bedeutet &amp;lt;i&amp;gt;um 1 erniedrigen&amp;lt;/i&amp;gt;). Anstatt also Befehle im Programm vorzusehen, die regelmäßig ausgeführt werden und ein Register inkrementieren, erledigt dies der Mikrocontroller ganz von alleine. Dazu ist es möglich, den Timer mit dem Systemtakt zu verbinden und so die Genauigkeit des Quarzes auszunutzen, um ein Register regelmässig und vor allen Dingen unabhängig vom restlichen Programmfluss (!) hochzählen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Davon alleine hätte man aber noch keinen großen Gewinn. Nützlich wird das Ganze erst dann, wenn man bei bestimmten Zählerständen eine Aktion ausführen lassen kann. Einer der &#039;bestimmten Zählerstände&#039; ist zum Beispiel der &#039;&#039;&#039;Overflow&#039;&#039;&#039;. Das Zählregister eines Timers kann natürlich nicht beliebig lange inkrementiert werden – z. B. ist der höchste Zählerstand, den ein 8-Bit-Timer erreichen kann, 2&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt; – 1 = 255. Beim nächsten Inkrementierschritt tritt ein Überlauf (engl. Overflow) auf, der den Timerstand wieder zu 0 werden lässt. Und hier liegt der springende Punkt. Wir können uns nämlich an diesen Overflow &amp;quot;anhängen&amp;quot; und den Controller so konfigurieren, dass beim Auftreten des Timer-Overflows ein Interrupt ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==Der Vorteiler (Prescaler)==&lt;br /&gt;
&lt;br /&gt;
Wenn also der Quarzoszillator mit 4 MHz schwingt, dann würde auch der Timer 4 Millionen mal in der Sekunde erhöht werden. Da der Timer jedes Mal von 0 bis 255 zählt, bevor ein Overflow auftritt, heist das auch, dass in einer Sekunde 4000000 / 256 = 15625 Overflows vorkommen. Ganz schön schnell! Nur: Oft ist das nicht sinnvoll. Um diese Raten zu verzögern, gibt es den Vorteiler, oder auf Englisch, Prescaler. Er kann auf die Werte 1, 8, 64, 256 oder 1024 eingestellt werden. Seine Aufgabe ist es, den Systemtakt um den angegebenen Faktor zu teilen. Steht der Vorteiler also auf 1024, so wird nur bei jedem 1024-ten Impuls vom Systemtakt das Timerregister um 1 erhöht. Entsprechend weniger häufig kommen dann natürlich die Overflows. Der Systemtakt sei wieder 4000000. Dann wird der Timer in 1 Sekunde 4000000 / 1024 = 3906.25 mal erhöht. Da der Timer wieder jedesmal bis 255 zählen muss bis ein Overflow auftritt, bedeutet dies, dass in 1 Sekunde 3906.25 / 256 = 15.25 Overflows auftreten.&lt;br /&gt;
&lt;br /&gt;
Systemtakt: 4Mhz&lt;br /&gt;
&lt;br /&gt;
  Vorteiler    Overflows/Sekunde      Zeit zwischen&lt;br /&gt;
                                      2 Overflows [s]&lt;br /&gt;
&lt;br /&gt;
      1            15625                0.000064&lt;br /&gt;
      8             1953.125            0.000512&lt;br /&gt;
     64              244.1406           0.004096&lt;br /&gt;
    256               61.0351           0.016384&lt;br /&gt;
   1024               15.2587           0.065536&lt;br /&gt;
&lt;br /&gt;
==Erste Tests==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das einen Timer Overflow in Aktion zeigt, könnte z.B. so aussehen:&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 leds = r17&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, 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, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     leds, 0xFF&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, 0b00000001      ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&lt;br /&gt;
        sei&lt;br /&gt;
&lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
timer0_overflow:                      ; Timer 0 Overflow Handler&lt;br /&gt;
        out     PORTB, leds&lt;br /&gt;
        com     leds&lt;br /&gt;
        reti&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm beginnt mit der [[AVR-Tutorial:_Interrupts|Interrupt-Vektoren-Tabelle]]. Dort ist an der Adresse &amp;lt;i&amp;gt;OVF0Addr&amp;lt;/i&amp;gt; ein Sprung zur Marke &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt; eingetragen. Wenn also ein Overflow Interrupt vom Timer 0 auftritt, so wird dieser Interrupt durch den &#039;&#039;&#039;rjmp&#039;&#039;&#039; weitergeleitet an die Stelle &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Hauptprogramm beginnt ganz normal mit der Belegung des Stackpointers. Danach wird der Port B auf Ausgang geschaltet, wir wollen hier wieder die LED anschliessen.&lt;br /&gt;
&lt;br /&gt;
Durch Beschreiben von TCCR0 mit dem Bitmuster 0b00000001 wird der Vorteiler auf 1 gesetzt. Für die ersten Versuche empfiehlt es sich, das Programm mit dem AVR-Studio zunächst zu simulieren. Würden wir einen größeren Vorteiler benutzen, so müsste man ziemlich oft mittels F11 einen simulierten Schritt ausführen, um eine Änderung im Timerregister zu erreichen.&lt;br /&gt;
&lt;br /&gt;
Die nächsten Anweisungen setzen im TIMSK Register das TOIE0 Bit. Sinn der Sache ist es, dem Timer zu erlauben, bei Erreichen eines Overflow einen Interrupt auszulösen.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss noch die Interrupts generell mittels &#039;&#039;&#039;sei&#039;&#039;&#039; freigeben. Dieser Schritt ist obligatorisch. Im Mikrocontroller können viele Quellen einen Interrupt auslösen. Daraus folgt: Für jede mögliche Quelle muss festgelegt werden, ob sie einen Interrupt erzeugen darf oder nicht. Die Oberhoheit hat aber das globale Interrupt Flag. Mit ihm können alle Interrupts, egal von welcher Quelle sie kommen, unterdrückt werden.&lt;br /&gt;
&lt;br /&gt;
Damit ist die Initialisierung beendet und das Hauptprogramm kann sich schlafen legen. Die &#039;&#039;&#039;loop: rjmp loop&#039;&#039;&#039; Schleife macht genau dieses.&lt;br /&gt;
&lt;br /&gt;
Tritt nun ein Overflow am Timer auf, so wird über den Umweg über die Interrupt Vektor Tabelle der Programmteil &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt; angesprungen. Dieser gibt einfach nur den Inhalt des Registers leds am Port B aus. Danach wird das leds Register mit einer &#039;&#039;&#039;com&#039;&#039;&#039; Operation negiert, so dass aus allen 0 Bits eine 1 wird und umgekehrt. Die Overflow Behandlung ist damit beendet und mittels &#039;&#039;&#039;reti&#039;&#039;&#039; wird der Interrupt Handler wieder verlassen.&lt;br /&gt;
&lt;br /&gt;
==Simulation im AVR-Studio==&lt;br /&gt;
&lt;br /&gt;
Es lohnt sich, den ganzen Vorgang im AVR-Studio simulieren zu lassen. Dazu am besten in der linken I/O View Ansicht die Einträge für PORTB und TIMER_COUNTER_0 öffnen. Wird mittels F11 durch das Programm gegangen, so sieht man, dass ab dem Moment, ab dem der Vorteiler auf 1 gesetzt wird, der Timer 0 im TCNT0 Register zu zählen anfängt. Mit jedem Druck auf F11 erhöht sich der Zählerstand. Irgendwann ist dann die Endlosschleife loop erreicht. Drücken Sie weiterhin F11 und beobachten sie, wie TCNT0 immer höher zählt, bis der Overflow erreicht wird. In dem Moment, in dem der Overflow erreicht wird, wird der Interrupt ausgelöst. Mit dem nächsten F11 landen sie in der Interrupt Vektor Tabelle und von dort geht es weiter zu timer_0_overflow. Weitere Tastendrücke von F11 erledigen dann die Ausgabe auf den Port B, das Invertieren des Registers r17 und der Interrupt ist damit behandelt. Nach dem &#039;&#039;&#039;reti&#039;&#039;&#039; macht der Microcontroller genau an der Stelle weiter, an der er vom Interrupt unterbrochen wurde. Und der Timer 0 hat in der Zwischenzeit weitergezählt! Nach exakt weiteren 256 Schritten, vom Auftreten des ersten Overflows an gerechnet, wird der nächste Overflow ausgelöst.&lt;br /&gt;
&lt;br /&gt;
==Wie schnell schaltet denn jetzt der Port?==&lt;br /&gt;
&lt;br /&gt;
Eine berechtigte Frage. Dazu müssen wir etwas rechnen. Keine Angst, es ist nicht schwer, und wer das Prinzip bisher verstanden hat, der sollte keine Schwierigkeiten haben die Berechnung nachzuvollziehen.&lt;br /&gt;
&lt;br /&gt;
Der Quarzoszillator schwingt mit 4 MHz. Das heißt in 1 Sekunde werden 4000000 Taktzyklen generiert. Durch die Wahl des Vorteilers von 1 bedeutet das auch, dass der Timer 4000000 mal in der Sekunde erhöht wird. Von einem Overflow zum nächsten muss der Timer 256 Zählvorgänge ausführen. Also werden in 1 Sekunde 4000000 / 256 = 15625 Overflows generiert. Bei jedem Overflow schalten wir die LEDs jeweils in den anderen Zustand. D.h die LEDs blinken mit einer Frequenz von 7812.5 Hz. Das ist zuviel als dass wir es noch sehen könnten.&lt;br /&gt;
&lt;br /&gt;
Was können wir also tun um diese Blinkfrequenz zu verringern? Im Moment ist unsere einzige Einflussgröße der Vorteiler. Wie sieht die Rechnung aus, wenn wir einen Vorteiler von 1024 wählen?&lt;br /&gt;
&lt;br /&gt;
Wiederrum: Der Systemtakt sei 4 Mhz. Durch den Vorteiler von 1024 werden daraus 4000000 / 1024 = 3906.25 Pulse pro Sekunde für den Timer. Der zählt wiederum 256 Zustände von einem Overflow zum nächsten. 3906.25 / 256 = 15.2587. Und wiederum: Im Overflow werden die LEDs ja abwechselnd ein und ausgeschaltet, also dividieren wir noch durch 2: 15.2587 / 2 = 7.629. Also knapp 7 Hz. Diese Frequenz müsste man schon mit freiem Auge sehen. Die LEDs werden ziemlich schnell vor sich hin blinken.&lt;br /&gt;
&lt;br /&gt;
Reicht diese Verzögerung noch immer nicht, dann haben wir 2 Möglichkeiten:&lt;br /&gt;
* Entweder wir benutzen einen anderen Timer. Timer 1 beispielsweise ist ein 16 Bit Timer. Der Timer zählt also nicht bis 256 sondern bis 65536. Bei entsprechender Umarbeitung des Programms und einem Vorteiler von 1024 bedeutet das, dass die LEDs einen Ein/Aus Zyklus in 33 Sekunden absolvieren.&lt;br /&gt;
* Oder wir schalten die LEDs nicht bei jedem Timer Overflow um. Man könnte zum Beispiel in einem Register bis 7 zählen und nur dann, wenn dieses Register 7 erreicht hat, wird&lt;br /&gt;
** das Register wieder auf 0 gesetzt und&lt;br /&gt;
** die LEDs umgeschaltet.&lt;br /&gt;
&lt;br /&gt;
==Timer 0==&lt;br /&gt;
&lt;br /&gt;
Timer 0 ist ein 8 Bit Timer.&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
&lt;br /&gt;
===TCCR0===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      | CS02 | CS01 | CS00 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS02 - CS00   Clock Select&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;CS02&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;CS01&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;CS00&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;keine (Der Timer ist angehalten)&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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:    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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:    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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:   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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  256&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler: 1024&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Externer Takt vom Pin T0, fallende Flanke&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Externer Takt vom Pin T0, steigende Flanke&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;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      | TOIE0|&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE0         Timer 0 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Ist dieses Bit gesetzt, so wird beim Auftreten eines&lt;br /&gt;
    Overflows am Timer ein Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Anstatt der Schreibweise&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
ist es besser, die Schreibweise&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi     temp, 1 &amp;lt;&amp;lt; TOIE0&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu wählen, da hier unmittelbar aus dem Ladekommando hervorgeht, welche Bedeutung das gesetzte Bit hat. Die vorher inkludierte m8def.inc definiert dazu alles Notwendige.&lt;br /&gt;
&lt;br /&gt;
==Timer 1==&lt;br /&gt;
&lt;br /&gt;
Timer 1 ist ein 16 Bit Timer&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
* Clear Timer on Compare Match&lt;br /&gt;
* Input Capture&lt;br /&gt;
* 2 Compare Einheiten&lt;br /&gt;
* div. PWM Modi&lt;br /&gt;
&lt;br /&gt;
===TCCR1B===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | ICNC1| ICES1|      | WGM13| WGM12| CS12 | CS11 | CS10 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS12 - CS10   Clock Select&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&amp;gt;CS12&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS11&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS10&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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;keine (Der Timer ist angehalten)&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;Vorteiler:    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;&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;Vorteiler:    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;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;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  256&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;Vorteiler: 1024&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;Externer Takt vom Pin T1, fallende Flanke&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;Externer Takt vom Pin T1, steigende Flanke&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;
  &amp;lt;i&amp;gt;ICES1   Input Capture Edge Select&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;ICNC1   Input Capture Noise Canceler&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TCCR1A===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |COM1A1|COM1A0|COM1B1|COM1B0|FOC1A |FOC1B |WGM11 |WGM10 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===OCR1A===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===OCR1B===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===ICR1===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |TICIE1|OCIE1A|OCIE1B| TOIE1|      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TICIE1    Timer 1 Input Capture Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE1A    Timer 1 Output Compare A Match Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE1B    Timer 1 Output Compare B Match Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE1     Timer 1 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Timer 2==&lt;br /&gt;
Timer 2 ist ein 8 Bit Timer.&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
* Compare Match Interrupt&lt;br /&gt;
* Clear Timer on Compare Match&lt;br /&gt;
* Phasen korrekte PWM&lt;br /&gt;
&lt;br /&gt;
===TCCR2===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | FOC2 | WGM20| COM21| COM20| WGM21| CS22 | CS21 | CS20 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS22 - CS20   Clock Select&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&amp;gt;CS22&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS21&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS20&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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;keine (Der Timer ist angehalten)&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;Vorteiler:    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;&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;Vorteiler:    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;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;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  128&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;Vorteiler:  256&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;Vorteiler: 1024&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;
===OCR2===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | OCIE2| TOIE2|      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE2   Timer 2 Output Compare Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE2   Timer 2 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Was geht noch mit einem Timer?==&lt;br /&gt;
&lt;br /&gt;
Timer sind sehr universelle Microcontroller Bestandteile. Für weitergehende Studien ist es daher unerlässlich das entsprechende Datenblatt des Microcontrollers zu studieren. Oft ist es zb. möglich, dass der Timer bei erreichen von bestimmten Zählerständen einen Ausgabepin von sich aus ein-/aus-/umschaltet. Er erledigt dann das was wir oben noch mit einem Interrupt gemacht haben eigenständig komplett in Hardware. Bei einigen Timern ist es möglich damit eine [[PWM]] (Pulsweiten-Modulation) aufzubauen.&lt;br /&gt;
&lt;br /&gt;
Ein paar der Timermodule lassen sich auch als Counter verwenden. Damit kann man z.B. die Anzahl externer Ereignisse wie Schaltvorgänge eines Inkrementalgebers bestimmen.&lt;br /&gt;
&lt;br /&gt;
Andere bieten die Möglichkeit über einen externen Uhrenquarz getaktet zu werden. (Anwendung z.B. eine &amp;quot;Echtzeituhr&amp;quot; oder als &amp;quot;Weckfunktion&amp;quot; aus einem&lt;br /&gt;
Standby/Powerdownmodus)&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* Timer/Counter und PWM beim ATMega16 Mikrocontroller: http://www.uni-koblenz.de/~physik/informatik/MCU/Timer.pdf &lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Speicher|&lt;br /&gt;
zurücklink=AVR-Tutorial: Speicher|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Uhr|&lt;br /&gt;
vorlink=AVR-Tutorial: Uhr}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=28094</id>
		<title>AVR-Tutorial: Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=28094"/>
		<updated>2008-05-22T23:12:41Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Erste Tests */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Timer sind eines der Hauptarbeitspferde in unserem Mikrocontroller. Mit ihrer Hilfe ist es möglich, in regelmäßigen Zeitabständen Aktionen zu veranlassen. Aber Timer können noch mehr!&lt;br /&gt;
* Timer können mit einem externen Pin hochgezählt werden&lt;br /&gt;
* Es gibt Möglichkeiten, bei bestimmten Zählerständen einen Interrupt auslösen zu lassen&lt;br /&gt;
* Timer können aber auch völlig selbstständig Signale an einem Ausgabepin erzeugen&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
Aber der Reihe nach.&lt;br /&gt;
&lt;br /&gt;
==Was ist ein Timer?==&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist im Grunde nichts anderes als ein bestimmtes Register im Mikrocontroller, das hardwaregesteuert fortlaufend um 1 erhöht (oder erniedrigt) wird (statt &amp;lt;i&amp;gt;um 1 erhöhen&amp;lt;/i&amp;gt; sagt man auch &#039;&#039;&#039;inkrementieren&#039;&#039;&#039;, und das Gegenstück, &#039;&#039;&#039;dekrementieren&#039;&#039;&#039;, bedeutet &amp;lt;i&amp;gt;um 1 erniedrigen&amp;lt;/i&amp;gt;). Anstatt also Befehle im Programm vorzusehen, die regelmäßig ausgeführt werden und ein Register inkrementieren, erledigt dies der Mikrocontroller ganz von alleine. Dazu ist es möglich, den Timer mit dem Systemtakt zu verbinden und so die Genauigkeit des Quarzes auszunutzen, um ein Register regelmässig und vor allen Dingen unabhängig vom restlichen Programmfluss (!) hochzählen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Davon alleine hätte man aber noch keinen großen Gewinn. Nützlich wird das Ganze erst dann, wenn man bei bestimmten Zählerständen eine Aktion ausführen lassen kann. Einer der &#039;bestimmten Zählerstände&#039; ist zum Beispiel der &#039;&#039;&#039;Overflow&#039;&#039;&#039;. Das Zählregister eines Timers kann natürlich nicht beliebig lange inkrementiert werden – z. B. ist der höchste Zählerstand, den ein 8-Bit-Timer erreichen kann, 2&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt; – 1 = 255. Beim nächsten Inkrementierschritt tritt ein Überlauf (engl. Overflow) auf, der den Timerstand wieder zu 0 werden lässt. Und hier liegt der springende Punkt. Wir können uns nämlich an diesen Overflow &amp;quot;anhängen&amp;quot; und den Controller so konfigurieren, dass beim Auftreten des Timer-Overflows ein Interrupt ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==Der Vorteiler (Prescaler)==&lt;br /&gt;
&lt;br /&gt;
Wenn also der Quarzoszillator mit 4 MHz schwingt, dann würde auch der Timer 4 Millionen mal in der Sekunde erhöht werden. Da der Timer jedes Mal von 0 bis 255 zählt, bevor ein Overflow auftritt, heist das auch, dass in einer Sekunde 4000000 / 256 = 15625 Overflows vorkommen. Ganz schön schnell! Nur: Oft ist das nicht sinnvoll. Um diese Raten zu verzögern, gibt es den Vorteiler, oder auf Englisch, Prescaler. Er kann auf die Werte 1, 8, 64, 256 oder 1024 eingestellt werden. Seine Aufgabe ist es, den Systemtakt um den angegebenen Faktor zu teilen. Steht der Vorteiler also auf 1024, so wird nur bei jedem 1024-ten Impuls vom Systemtakt das Timerregister um 1 erhöht. Entsprechend weniger häufig kommen dann natürlich die Overflows. Der Systemtakt sei wieder 4000000. Dann wird der Timer in 1 Sekunde 4000000 / 1024 = 3906.25 mal erhöht. Da der Timer wieder jedesmal bis 255 zählen muss bis ein Overflow auftritt, bedeutet dies, dass in 1 Sekunde 3906.25 / 256 = 15.25 Overflows auftreten.&lt;br /&gt;
&lt;br /&gt;
Systemtakt: 4Mhz&lt;br /&gt;
&lt;br /&gt;
  Vorteiler    Overflows/Sekunde      Zeit zwischen&lt;br /&gt;
                                      2 Overflows [s]&lt;br /&gt;
&lt;br /&gt;
      1            15625                0.000064&lt;br /&gt;
      8             1953.125            0.000512&lt;br /&gt;
     64              244.1406           0.004096&lt;br /&gt;
    256               61.0351           0.016384&lt;br /&gt;
   1024               15.2587           0.065536&lt;br /&gt;
&lt;br /&gt;
==Erste Tests==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das einen Timer Overflow in Aktion zeigt, könnte z.B. so aussehen:&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 leds = r17&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, 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, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     leds, 0xFF&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, 0b00000001      ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&lt;br /&gt;
        sei&lt;br /&gt;
&lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
timer0_overflow:                      ; Timer 0 Overflow Handler&lt;br /&gt;
        out     PORTB, leds&lt;br /&gt;
        com     leds&lt;br /&gt;
        reti&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm beginnt mit der [[AVR-Tutorial:_Interrupts|Interrupt-Vektoren-Tabelle]]. Dort ist an der Adresse &amp;lt;i&amp;gt;OVF0Addr&amp;lt;/i&amp;gt; ein Sprung zur Marke &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt; eingetragen. Wenn also ein Overflow Interrupt vom Timer 0 auftritt, so wird dieser Interrupt durch den &#039;&#039;&#039;rjmp&#039;&#039;&#039; weitergeleitet an die Stelle &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Hauptprogramm beginnt ganz normal mit der Belegung des Stackpointers. Danach wird der Port B auf Ausgang geschaltet, wir wollen hier wieder die LED anschliessen.&lt;br /&gt;
&lt;br /&gt;
Durch Beschreiben von TCCR0 mit dem Bitmuster 0b00000001 wird der Vorteiler auf 1 gesetzt. Für die ersten Versuche empfiehlt es sich, das Programm mit dem AVR-Studio zunächst zu simulieren. Würden wir einen größeren Vorteiler benutzen, so müsste man ziemlich oft mittels F11 einen simulierten Schritt ausführen, um eine Änderung im Timerregister zu erreichen.&lt;br /&gt;
&lt;br /&gt;
Die nächsten Anweisungen setzen im TIMSK Register das TOIE0 Bit. Sinn der Sache ist es, dem Timer zu erlauben, bei Erreichen eines Overflow einen Interrupt auszulösen.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss noch die Interrupts generell mittels &#039;&#039;&#039;sei&#039;&#039;&#039; freigeben. Dieser Schritt ist obligatorisch. Im Mikrocontroller können viele Quellen einen Interrupt auslösen. Daraus folgt: Für jede mögliche Quelle muss festgelegt werden, ob sie einen Interrupt erzeugen darf oder nicht. Die Oberhoheit hat aber das globale Interrupt Flag. Mit ihm können alle Interrupts, egal von welcher Quelle sie kommen, unterdrückt werden.&lt;br /&gt;
&lt;br /&gt;
Damit ist die Initialisierung beendet und das Hauptprogramm kann sich schlafen legen. Die &#039;&#039;&#039;loop: rjmp loop&#039;&#039;&#039; Schleife macht genau dieses.&lt;br /&gt;
&lt;br /&gt;
Tritt nun ein Overflow am Timer auf, so wird über den Umweg über die Interrupt Vektor Tabelle der Programmteil &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt; angesprungen. Dieser gibt einfach nur den Inhalt des Registers leds am Port B aus. Danach wird das leds Register mit einer &#039;&#039;&#039;com&#039;&#039;&#039; Operation negiert, so dass aus allen 0 Bits eine 1 wird und umgekehrt. Die Overflow Behandlung ist damit beendet und mittels &#039;&#039;&#039;reti&#039;&#039;&#039; wird der Interrupt Handler wieder verlassen.&lt;br /&gt;
&lt;br /&gt;
==Simulation im AVR-Studio==&lt;br /&gt;
&lt;br /&gt;
Es lohnt sich den ganzen Vorgang im AVR-Studio simulieren zu lassen. Dazu am besten in der linken I/O View Ansicht die Einträge für PORTB und TIMER_COUNTER_0 öffnen. Wird mittels F11 durch das Programm gegangen, so sieht man, dass ab dem Moment, ab dem der Vorteiler auf 1 gesetzt wird, der Timer 0 im TCNT0 Register zu zählen anfängt. Mit jedem Druck auf F11 erhöht sich der Zählerstand. Irgendwann ist dann die Endlosschleife loop erreicht. Drücken Sie weiterhin F11 und beobachten sie, wie TCNT0 immer höher zählt, bis der Overflow erreicht wird. In dem Moment, in dem der Overflow erreicht wird, wird der Interrupt ausgelöst. Mit dem nächsten F11 landen sie in der Interrupt Vektor Tabelle und von dort geht es weiter zu timer_0_overflow. Weitere Tastendrücke von F11 erledigen dann die Ausgabe auf den Port B, das invertieren des Registers r17 und der Interrupt ist damit behandelt. Nach dem &#039;&#039;&#039;reti&#039;&#039;&#039; macht der Microcontroller genau an der Stelle weiter, an der er vom Interrupt unterbrochen wurde. Und der Timer 0 hat in der Zwischenzeit weitergezählt! Nach exakt weiteren 256 Schritten, vom Auftreten des ersten Overflows an gerechnet, wird der nächste Overflow ausgelöst.&lt;br /&gt;
&lt;br /&gt;
==Wie schnell schaltet denn jetzt der Port?==&lt;br /&gt;
&lt;br /&gt;
Eine berechtigte Frage. Dazu müssen wir etwas rechnen. Keine Angst, es ist nicht schwer, und wer das Prinzip bisher verstanden hat, der sollte keine Schwierigkeiten haben die Berechnung nachzuvollziehen.&lt;br /&gt;
&lt;br /&gt;
Der Quarzoszillator schwingt mit 4 MHz. Das heißt in 1 Sekunde werden 4000000 Taktzyklen generiert. Durch die Wahl des Vorteilers von 1 bedeutet das auch, dass der Timer 4000000 mal in der Sekunde erhöht wird. Von einem Overflow zum nächsten muss der Timer 256 Zählvorgänge ausführen. Also werden in 1 Sekunde 4000000 / 256 = 15625 Overflows generiert. Bei jedem Overflow schalten wir die LEDs jeweils in den anderen Zustand. D.h die LEDs blinken mit einer Frequenz von 7812.5 Hz. Das ist zuviel als dass wir es noch sehen könnten.&lt;br /&gt;
&lt;br /&gt;
Was können wir also tun um diese Blinkfrequenz zu verringern? Im Moment ist unsere einzige Einflussgröße der Vorteiler. Wie sieht die Rechnung aus, wenn wir einen Vorteiler von 1024 wählen?&lt;br /&gt;
&lt;br /&gt;
Wiederrum: Der Systemtakt sei 4 Mhz. Durch den Vorteiler von 1024 werden daraus 4000000 / 1024 = 3906.25 Pulse pro Sekunde für den Timer. Der zählt wiederum 256 Zustände von einem Overflow zum nächsten. 3906.25 / 256 = 15.2587. Und wiederum: Im Overflow werden die LEDs ja abwechselnd ein und ausgeschaltet, also dividieren wir noch durch 2: 15.2587 / 2 = 7.629. Also knapp 7 Hz. Diese Frequenz müsste man schon mit freiem Auge sehen. Die LEDs werden ziemlich schnell vor sich hin blinken.&lt;br /&gt;
&lt;br /&gt;
Reicht diese Verzögerung noch immer nicht, dann haben wir 2 Möglichkeiten:&lt;br /&gt;
* Entweder wir benutzen einen anderen Timer. Timer 1 beispielsweise ist ein 16 Bit Timer. Der Timer zählt also nicht bis 256 sondern bis 65536. Bei entsprechender Umarbeitung des Programms und einem Vorteiler von 1024 bedeutet das, dass die LEDs einen Ein/Aus Zyklus in 33 Sekunden absolvieren.&lt;br /&gt;
* Oder wir schalten die LEDs nicht bei jedem Timer Overflow um. Man könnte zum Beispiel in einem Register bis 7 zählen und nur dann, wenn dieses Register 7 erreicht hat, wird&lt;br /&gt;
** das Register wieder auf 0 gesetzt und&lt;br /&gt;
** die LEDs umgeschaltet.&lt;br /&gt;
&lt;br /&gt;
==Timer 0==&lt;br /&gt;
&lt;br /&gt;
Timer 0 ist ein 8 Bit Timer.&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
&lt;br /&gt;
===TCCR0===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      | CS02 | CS01 | CS00 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS02 - CS00   Clock Select&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;CS02&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;CS01&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;CS00&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;keine (Der Timer ist angehalten)&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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:    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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:    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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:   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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  256&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler: 1024&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Externer Takt vom Pin T0, fallende Flanke&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Externer Takt vom Pin T0, steigende Flanke&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;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      | TOIE0|&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE0         Timer 0 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Ist dieses Bit gesetzt, so wird beim Auftreten eines&lt;br /&gt;
    Overflows am Timer ein Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Anstatt der Schreibweise&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
ist es besser, die Schreibweise&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi     temp, 1 &amp;lt;&amp;lt; TOIE0&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu wählen, da hier unmittelbar aus dem Ladekommando hervorgeht, welche Bedeutung das gesetzte Bit hat. Die vorher inkludierte m8def.inc definiert dazu alles Notwendige.&lt;br /&gt;
&lt;br /&gt;
==Timer 1==&lt;br /&gt;
&lt;br /&gt;
Timer 1 ist ein 16 Bit Timer&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
* Clear Timer on Compare Match&lt;br /&gt;
* Input Capture&lt;br /&gt;
* 2 Compare Einheiten&lt;br /&gt;
* div. PWM Modi&lt;br /&gt;
&lt;br /&gt;
===TCCR1B===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | ICNC1| ICES1|      | WGM13| WGM12| CS12 | CS11 | CS10 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS12 - CS10   Clock Select&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&amp;gt;CS12&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS11&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS10&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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;keine (Der Timer ist angehalten)&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;Vorteiler:    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;&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;Vorteiler:    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;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;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  256&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;Vorteiler: 1024&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;Externer Takt vom Pin T1, fallende Flanke&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;Externer Takt vom Pin T1, steigende Flanke&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;
  &amp;lt;i&amp;gt;ICES1   Input Capture Edge Select&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;ICNC1   Input Capture Noise Canceler&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TCCR1A===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |COM1A1|COM1A0|COM1B1|COM1B0|FOC1A |FOC1B |WGM11 |WGM10 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===OCR1A===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===OCR1B===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===ICR1===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |TICIE1|OCIE1A|OCIE1B| TOIE1|      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TICIE1    Timer 1 Input Capture Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE1A    Timer 1 Output Compare A Match Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE1B    Timer 1 Output Compare B Match Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE1     Timer 1 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Timer 2==&lt;br /&gt;
Timer 2 ist ein 8 Bit Timer.&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
* Compare Match Interrupt&lt;br /&gt;
* Clear Timer on Compare Match&lt;br /&gt;
* Phasen korrekte PWM&lt;br /&gt;
&lt;br /&gt;
===TCCR2===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | FOC2 | WGM20| COM21| COM20| WGM21| CS22 | CS21 | CS20 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS22 - CS20   Clock Select&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&amp;gt;CS22&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS21&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS20&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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;keine (Der Timer ist angehalten)&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;Vorteiler:    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;&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;Vorteiler:    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;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;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  128&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;Vorteiler:  256&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;Vorteiler: 1024&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;
===OCR2===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | OCIE2| TOIE2|      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE2   Timer 2 Output Compare Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE2   Timer 2 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Was geht noch mit einem Timer?==&lt;br /&gt;
&lt;br /&gt;
Timer sind sehr universelle Microcontroller Bestandteile. Für weitergehende Studien ist es daher unerlässlich das entsprechende Datenblatt des Microcontrollers zu studieren. Oft ist es zb. möglich, dass der Timer bei erreichen von bestimmten Zählerständen einen Ausgabepin von sich aus ein-/aus-/umschaltet. Er erledigt dann das was wir oben noch mit einem Interrupt gemacht haben eigenständig komplett in Hardware. Bei einigen Timern ist es möglich damit eine [[PWM]] (Pulsweiten-Modulation) aufzubauen.&lt;br /&gt;
&lt;br /&gt;
Ein paar der Timermodule lassen sich auch als Counter verwenden. Damit kann man z.B. die Anzahl externer Ereignisse wie Schaltvorgänge eines Inkrementalgebers bestimmen.&lt;br /&gt;
&lt;br /&gt;
Andere bieten die Möglichkeit über einen externen Uhrenquarz getaktet zu werden. (Anwendung z.B. eine &amp;quot;Echtzeituhr&amp;quot; oder als &amp;quot;Weckfunktion&amp;quot; aus einem&lt;br /&gt;
Standby/Powerdownmodus)&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* Timer/Counter und PWM beim ATMega16 Mikrocontroller: http://www.uni-koblenz.de/~physik/informatik/MCU/Timer.pdf &lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Speicher|&lt;br /&gt;
zurücklink=AVR-Tutorial: Speicher|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Uhr|&lt;br /&gt;
vorlink=AVR-Tutorial: Uhr}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=28093</id>
		<title>AVR-Tutorial: Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=28093"/>
		<updated>2008-05-22T23:07:29Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Was ist ein Timer? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Timer sind eines der Hauptarbeitspferde in unserem Mikrocontroller. Mit ihrer Hilfe ist es möglich, in regelmäßigen Zeitabständen Aktionen zu veranlassen. Aber Timer können noch mehr!&lt;br /&gt;
* Timer können mit einem externen Pin hochgezählt werden&lt;br /&gt;
* Es gibt Möglichkeiten, bei bestimmten Zählerständen einen Interrupt auslösen zu lassen&lt;br /&gt;
* Timer können aber auch völlig selbstständig Signale an einem Ausgabepin erzeugen&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
Aber der Reihe nach.&lt;br /&gt;
&lt;br /&gt;
==Was ist ein Timer?==&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist im Grunde nichts anderes als ein bestimmtes Register im Mikrocontroller, das hardwaregesteuert fortlaufend um 1 erhöht (oder erniedrigt) wird (statt &amp;lt;i&amp;gt;um 1 erhöhen&amp;lt;/i&amp;gt; sagt man auch &#039;&#039;&#039;inkrementieren&#039;&#039;&#039;, und das Gegenstück, &#039;&#039;&#039;dekrementieren&#039;&#039;&#039;, bedeutet &amp;lt;i&amp;gt;um 1 erniedrigen&amp;lt;/i&amp;gt;). Anstatt also Befehle im Programm vorzusehen, die regelmäßig ausgeführt werden und ein Register inkrementieren, erledigt dies der Mikrocontroller ganz von alleine. Dazu ist es möglich, den Timer mit dem Systemtakt zu verbinden und so die Genauigkeit des Quarzes auszunutzen, um ein Register regelmässig und vor allen Dingen unabhängig vom restlichen Programmfluss (!) hochzählen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Davon alleine hätte man aber noch keinen großen Gewinn. Nützlich wird das Ganze erst dann, wenn man bei bestimmten Zählerständen eine Aktion ausführen lassen kann. Einer der &#039;bestimmten Zählerstände&#039; ist zum Beispiel der &#039;&#039;&#039;Overflow&#039;&#039;&#039;. Das Zählregister eines Timers kann natürlich nicht beliebig lange inkrementiert werden – z. B. ist der höchste Zählerstand, den ein 8-Bit-Timer erreichen kann, 2&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt; – 1 = 255. Beim nächsten Inkrementierschritt tritt ein Überlauf (engl. Overflow) auf, der den Timerstand wieder zu 0 werden lässt. Und hier liegt der springende Punkt. Wir können uns nämlich an diesen Overflow &amp;quot;anhängen&amp;quot; und den Controller so konfigurieren, dass beim Auftreten des Timer-Overflows ein Interrupt ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==Der Vorteiler (Prescaler)==&lt;br /&gt;
&lt;br /&gt;
Wenn also der Quarzoszillator mit 4 MHz schwingt, dann würde auch der Timer 4 Millionen mal in der Sekunde erhöht werden. Da der Timer jedes Mal von 0 bis 255 zählt, bevor ein Overflow auftritt, heist das auch, dass in einer Sekunde 4000000 / 256 = 15625 Overflows vorkommen. Ganz schön schnell! Nur: Oft ist das nicht sinnvoll. Um diese Raten zu verzögern, gibt es den Vorteiler, oder auf Englisch, Prescaler. Er kann auf die Werte 1, 8, 64, 256 oder 1024 eingestellt werden. Seine Aufgabe ist es, den Systemtakt um den angegebenen Faktor zu teilen. Steht der Vorteiler also auf 1024, so wird nur bei jedem 1024-ten Impuls vom Systemtakt das Timerregister um 1 erhöht. Entsprechend weniger häufig kommen dann natürlich die Overflows. Der Systemtakt sei wieder 4000000. Dann wird der Timer in 1 Sekunde 4000000 / 1024 = 3906.25 mal erhöht. Da der Timer wieder jedesmal bis 255 zählen muss bis ein Overflow auftritt, bedeutet dies, dass in 1 Sekunde 3906.25 / 256 = 15.25 Overflows auftreten.&lt;br /&gt;
&lt;br /&gt;
Systemtakt: 4Mhz&lt;br /&gt;
&lt;br /&gt;
  Vorteiler    Overflows/Sekunde      Zeit zwischen&lt;br /&gt;
                                      2 Overflows [s]&lt;br /&gt;
&lt;br /&gt;
      1            15625                0.000064&lt;br /&gt;
      8             1953.125            0.000512&lt;br /&gt;
     64              244.1406           0.004096&lt;br /&gt;
    256               61.0351           0.016384&lt;br /&gt;
   1024               15.2587           0.065536&lt;br /&gt;
&lt;br /&gt;
==Erste Tests==&lt;br /&gt;
&lt;br /&gt;
Ein Programm das einen Timer Overflow in Aktion zeigt könnte z.B. so aussehen:&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 leds = r17&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, 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, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     leds, 0xFF&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, 0b00000001      ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&lt;br /&gt;
        sei&lt;br /&gt;
&lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
timer0_overflow:                      ; Timer 0 Overflow Handler&lt;br /&gt;
        out     PORTB, leds&lt;br /&gt;
        com     leds&lt;br /&gt;
        reti&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm beginnt mit der [[AVR-Tutorial:_Interrupts|Interrupt-Vektoren-Tabelle]]. Dort ist an der Adresse &amp;lt;i&amp;gt;OVF0Addr&amp;lt;/i&amp;gt; ein Sprung zur Marke &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt; eingetragen. Wenn also ein Overflow Interrupt vom Timer 0 auftritt, so wird dieser Interrupt durch den &#039;&#039;&#039;rjmp&#039;&#039;&#039; weitergeleitet an die Stelle &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Hauptprogramm beginnt ganz normal mit der Belegung des Stackpointers. Danach wird der Port B auf Ausgang geschaltet, wir wollen hier wieder die LED anschliessen.&lt;br /&gt;
&lt;br /&gt;
Durch Beschreiben von TCCR0 mit dem Bitmuster 0b00000001 wird der Vorteiler auf 1 gesetzt. Für die ersten Versuche empfiehlt es sich, das Programm mit dem AVR-Studio zunächst zu simulieren. Würden wir einen größeren Vorteiler benutzen, so müsste man ziemlich oft mittels F11 einen simulierten Schritt ausführen um eine Änderung im Timerregister zu erreichen.&lt;br /&gt;
&lt;br /&gt;
Die nächsten Anweisungen setzen im TIMSK Register das TOIE0 Bit. Sinn der Sache ist es, dem Timer zu erlauben bei Erreichen eines Overflow einen Interrupt auszulösen.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss noch die Interrupts generell mittels &#039;&#039;&#039;sei&#039;&#039;&#039; freigeben. Dieser Schritt ist obligatorisch. Im Mikrocontroller können viele Quellen einen Interrupt auslösen. Daraus folgt: Für jede mögliche Quelle muss festgelegt werden, ob sie einen Interrupt erzeugen darf oder nicht. Die Oberhoheit hat aber das globale Interrupt Flag. Mit ihm können alle Interrupts, egal von welcher Quelle sie kommen, unterdrückt werden.&lt;br /&gt;
&lt;br /&gt;
Damit ist die Initialisierung beendet und das Hauptprogramm kann sich schlafen legen. Die &#039;&#039;&#039;loop: rjmp loop&#039;&#039;&#039; Schleife macht genau dieses.&lt;br /&gt;
&lt;br /&gt;
Tritt nun ein Overflow am Timer auf, so wird über den Umweg über die Interrupt Vektor Tabelle der Programmteil &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt; angesprungen. Dieser gibt einfach nur den Inhalt des Registers leds am Port B aus. Danach wird das leds Register mit einer &#039;&#039;&#039;com&#039;&#039;&#039; Operation negiert, so dass aus allen 0 Bits eine 1 wird und umgekehrt. Die Overflow Behandlung ist damit beendet und mittels &#039;&#039;&#039;reti&#039;&#039;&#039; wird der Interrupt Handler wieder verlassen.&lt;br /&gt;
&lt;br /&gt;
==Simulation im AVR-Studio==&lt;br /&gt;
&lt;br /&gt;
Es lohnt sich den ganzen Vorgang im AVR-Studio simulieren zu lassen. Dazu am besten in der linken I/O View Ansicht die Einträge für PORTB und TIMER_COUNTER_0 öffnen. Wird mittels F11 durch das Programm gegangen, so sieht man, dass ab dem Moment, ab dem der Vorteiler auf 1 gesetzt wird, der Timer 0 im TCNT0 Register zu zählen anfängt. Mit jedem Druck auf F11 erhöht sich der Zählerstand. Irgendwann ist dann die Endlosschleife loop erreicht. Drücken Sie weiterhin F11 und beobachten sie, wie TCNT0 immer höher zählt, bis der Overflow erreicht wird. In dem Moment, in dem der Overflow erreicht wird, wird der Interrupt ausgelöst. Mit dem nächsten F11 landen sie in der Interrupt Vektor Tabelle und von dort geht es weiter zu timer_0_overflow. Weitere Tastendrücke von F11 erledigen dann die Ausgabe auf den Port B, das invertieren des Registers r17 und der Interrupt ist damit behandelt. Nach dem &#039;&#039;&#039;reti&#039;&#039;&#039; macht der Microcontroller genau an der Stelle weiter, an der er vom Interrupt unterbrochen wurde. Und der Timer 0 hat in der Zwischenzeit weitergezählt! Nach exakt weiteren 256 Schritten, vom Auftreten des ersten Overflows an gerechnet, wird der nächste Overflow ausgelöst.&lt;br /&gt;
&lt;br /&gt;
==Wie schnell schaltet denn jetzt der Port?==&lt;br /&gt;
&lt;br /&gt;
Eine berechtigte Frage. Dazu müssen wir etwas rechnen. Keine Angst, es ist nicht schwer, und wer das Prinzip bisher verstanden hat, der sollte keine Schwierigkeiten haben die Berechnung nachzuvollziehen.&lt;br /&gt;
&lt;br /&gt;
Der Quarzoszillator schwingt mit 4 MHz. Das heißt in 1 Sekunde werden 4000000 Taktzyklen generiert. Durch die Wahl des Vorteilers von 1 bedeutet das auch, dass der Timer 4000000 mal in der Sekunde erhöht wird. Von einem Overflow zum nächsten muss der Timer 256 Zählvorgänge ausführen. Also werden in 1 Sekunde 4000000 / 256 = 15625 Overflows generiert. Bei jedem Overflow schalten wir die LEDs jeweils in den anderen Zustand. D.h die LEDs blinken mit einer Frequenz von 7812.5 Hz. Das ist zuviel als dass wir es noch sehen könnten.&lt;br /&gt;
&lt;br /&gt;
Was können wir also tun um diese Blinkfrequenz zu verringern? Im Moment ist unsere einzige Einflussgröße der Vorteiler. Wie sieht die Rechnung aus, wenn wir einen Vorteiler von 1024 wählen?&lt;br /&gt;
&lt;br /&gt;
Wiederrum: Der Systemtakt sei 4 Mhz. Durch den Vorteiler von 1024 werden daraus 4000000 / 1024 = 3906.25 Pulse pro Sekunde für den Timer. Der zählt wiederum 256 Zustände von einem Overflow zum nächsten. 3906.25 / 256 = 15.2587. Und wiederum: Im Overflow werden die LEDs ja abwechselnd ein und ausgeschaltet, also dividieren wir noch durch 2: 15.2587 / 2 = 7.629. Also knapp 7 Hz. Diese Frequenz müsste man schon mit freiem Auge sehen. Die LEDs werden ziemlich schnell vor sich hin blinken.&lt;br /&gt;
&lt;br /&gt;
Reicht diese Verzögerung noch immer nicht, dann haben wir 2 Möglichkeiten:&lt;br /&gt;
* Entweder wir benutzen einen anderen Timer. Timer 1 beispielsweise ist ein 16 Bit Timer. Der Timer zählt also nicht bis 256 sondern bis 65536. Bei entsprechender Umarbeitung des Programms und einem Vorteiler von 1024 bedeutet das, dass die LEDs einen Ein/Aus Zyklus in 33 Sekunden absolvieren.&lt;br /&gt;
* Oder wir schalten die LEDs nicht bei jedem Timer Overflow um. Man könnte zum Beispiel in einem Register bis 7 zählen und nur dann, wenn dieses Register 7 erreicht hat, wird&lt;br /&gt;
** das Register wieder auf 0 gesetzt und&lt;br /&gt;
** die LEDs umgeschaltet.&lt;br /&gt;
&lt;br /&gt;
==Timer 0==&lt;br /&gt;
&lt;br /&gt;
Timer 0 ist ein 8 Bit Timer.&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
&lt;br /&gt;
===TCCR0===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      | CS02 | CS01 | CS00 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS02 - CS00   Clock Select&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;CS02&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;CS01&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;CS00&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;keine (Der Timer ist angehalten)&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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:    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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:    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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:   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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  256&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler: 1024&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Externer Takt vom Pin T0, fallende Flanke&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Externer Takt vom Pin T0, steigende Flanke&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;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      | TOIE0|&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE0         Timer 0 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Ist dieses Bit gesetzt, so wird beim Auftreten eines&lt;br /&gt;
    Overflows am Timer ein Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Anstatt der Schreibweise&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
ist es besser, die Schreibweise&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi     temp, 1 &amp;lt;&amp;lt; TOIE0&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu wählen, da hier unmittelbar aus dem Ladekommando hervorgeht, welche Bedeutung das gesetzte Bit hat. Die vorher inkludierte m8def.inc definiert dazu alles Notwendige.&lt;br /&gt;
&lt;br /&gt;
==Timer 1==&lt;br /&gt;
&lt;br /&gt;
Timer 1 ist ein 16 Bit Timer&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
* Clear Timer on Compare Match&lt;br /&gt;
* Input Capture&lt;br /&gt;
* 2 Compare Einheiten&lt;br /&gt;
* div. PWM Modi&lt;br /&gt;
&lt;br /&gt;
===TCCR1B===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | ICNC1| ICES1|      | WGM13| WGM12| CS12 | CS11 | CS10 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS12 - CS10   Clock Select&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&amp;gt;CS12&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS11&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS10&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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;keine (Der Timer ist angehalten)&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;Vorteiler:    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;&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;Vorteiler:    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;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;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  256&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;Vorteiler: 1024&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;Externer Takt vom Pin T1, fallende Flanke&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;Externer Takt vom Pin T1, steigende Flanke&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;
  &amp;lt;i&amp;gt;ICES1   Input Capture Edge Select&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;ICNC1   Input Capture Noise Canceler&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TCCR1A===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |COM1A1|COM1A0|COM1B1|COM1B0|FOC1A |FOC1B |WGM11 |WGM10 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===OCR1A===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===OCR1B===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===ICR1===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |TICIE1|OCIE1A|OCIE1B| TOIE1|      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TICIE1    Timer 1 Input Capture Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE1A    Timer 1 Output Compare A Match Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE1B    Timer 1 Output Compare B Match Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE1     Timer 1 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Timer 2==&lt;br /&gt;
Timer 2 ist ein 8 Bit Timer.&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
* Compare Match Interrupt&lt;br /&gt;
* Clear Timer on Compare Match&lt;br /&gt;
* Phasen korrekte PWM&lt;br /&gt;
&lt;br /&gt;
===TCCR2===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | FOC2 | WGM20| COM21| COM20| WGM21| CS22 | CS21 | CS20 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS22 - CS20   Clock Select&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&amp;gt;CS22&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS21&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS20&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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;keine (Der Timer ist angehalten)&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;Vorteiler:    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;&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;Vorteiler:    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;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;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  128&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;Vorteiler:  256&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;Vorteiler: 1024&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;
===OCR2===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | OCIE2| TOIE2|      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE2   Timer 2 Output Compare Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE2   Timer 2 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Was geht noch mit einem Timer?==&lt;br /&gt;
&lt;br /&gt;
Timer sind sehr universelle Microcontroller Bestandteile. Für weitergehende Studien ist es daher unerlässlich das entsprechende Datenblatt des Microcontrollers zu studieren. Oft ist es zb. möglich, dass der Timer bei erreichen von bestimmten Zählerständen einen Ausgabepin von sich aus ein-/aus-/umschaltet. Er erledigt dann das was wir oben noch mit einem Interrupt gemacht haben eigenständig komplett in Hardware. Bei einigen Timern ist es möglich damit eine [[PWM]] (Pulsweiten-Modulation) aufzubauen.&lt;br /&gt;
&lt;br /&gt;
Ein paar der Timermodule lassen sich auch als Counter verwenden. Damit kann man z.B. die Anzahl externer Ereignisse wie Schaltvorgänge eines Inkrementalgebers bestimmen.&lt;br /&gt;
&lt;br /&gt;
Andere bieten die Möglichkeit über einen externen Uhrenquarz getaktet zu werden. (Anwendung z.B. eine &amp;quot;Echtzeituhr&amp;quot; oder als &amp;quot;Weckfunktion&amp;quot; aus einem&lt;br /&gt;
Standby/Powerdownmodus)&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* Timer/Counter und PWM beim ATMega16 Mikrocontroller: http://www.uni-koblenz.de/~physik/informatik/MCU/Timer.pdf &lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Speicher|&lt;br /&gt;
zurücklink=AVR-Tutorial: Speicher|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Uhr|&lt;br /&gt;
vorlink=AVR-Tutorial: Uhr}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=28092</id>
		<title>AVR-Tutorial: Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=28092"/>
		<updated>2008-05-22T23:05:33Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Timer sind eines der Hauptarbeitspferde in unserem Mikrocontroller. Mit ihrer Hilfe ist es möglich, in regelmäßigen Zeitabständen Aktionen zu veranlassen. Aber Timer können noch mehr!&lt;br /&gt;
* Timer können mit einem externen Pin hochgezählt werden&lt;br /&gt;
* Es gibt Möglichkeiten, bei bestimmten Zählerständen einen Interrupt auslösen zu lassen&lt;br /&gt;
* Timer können aber auch völlig selbstständig Signale an einem Ausgabepin erzeugen&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
Aber der Reihe nach.&lt;br /&gt;
&lt;br /&gt;
==Was ist ein Timer?==&lt;br /&gt;
&lt;br /&gt;
Ein Timer ist im Grunde nichts anderes als ein bestimmtes Register im Mikrocontroller, das hardwaregesteuert fortlaufend um 1 erhöht (oder erniedrigt) wird (statt &amp;lt;i&amp;gt;um 1 erhöhen&amp;lt;/i&amp;gt; sagt man auch &#039;&#039;&#039;inkrementieren&#039;&#039;&#039;, und das Gegenstück, &#039;&#039;&#039;dekrementieren&#039;&#039;&#039;, bedeutet &amp;lt;i&amp;gt;um 1 erniedrigen&amp;lt;/i&amp;gt;). Anstatt also Befehle im Programm vorzusehen, die regelmäßig ausgeführt werden und ein Register inkrementieren, erledigt dies der Mikrocontroller ganz von alleine. Dazu ist es möglich, den Timer mit dem Systemtakt zu verbinden und so die Genauigkeit des Quarzes auszunutzen, um ein Register regelmässig und vor allen Dingen unabhängig vom restlichen Programmfluss (!) hochzählen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Davon alleine hätte man aber noch keinen großen Gewinn. Nützlich wird das Ganze erst dann, wenn man bei bestimmten Zählerständen eine Aktion ausführen lassen kann. Einer der &#039;bestimmten Zählerstände&#039; ist zum Beispiel der &#039;&#039;&#039;Overflow&#039;&#039;&#039;. Das Zählregister eines Timers kann natürlich nicht beliebig lange inkrementiert werden – z. B. ist der höchste Zählerstand, den ein 8-Bit-Timer erreichen kann, 2&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt; – 1 = 255. Beim nächsten Inkrementierschritt tritt ein Überlauf (engl. Overflow) auf, der den Timerstand wieder zu 0 werden lässt. Und hier liegt der springende Punkt. Wir können uns nämlich an diesen Overflow &amp;quot;anhängen&amp;quot; und den Controller so konfigurieren, daß beim Auftreten des Timer-Overflows ein Interrupt ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
==Der Vorteiler (Prescaler)==&lt;br /&gt;
&lt;br /&gt;
Wenn also der Quarzoszillator mit 4 MHz schwingt, dann würde auch der Timer 4 Millionen mal in der Sekunde erhöht werden. Da der Timer jedes Mal von 0 bis 255 zählt, bevor ein Overflow auftritt, heist das auch, dass in einer Sekunde 4000000 / 256 = 15625 Overflows vorkommen. Ganz schön schnell! Nur: Oft ist das nicht sinnvoll. Um diese Raten zu verzögern, gibt es den Vorteiler, oder auf Englisch, Prescaler. Er kann auf die Werte 1, 8, 64, 256 oder 1024 eingestellt werden. Seine Aufgabe ist es, den Systemtakt um den angegebenen Faktor zu teilen. Steht der Vorteiler also auf 1024, so wird nur bei jedem 1024-ten Impuls vom Systemtakt das Timerregister um 1 erhöht. Entsprechend weniger häufig kommen dann natürlich die Overflows. Der Systemtakt sei wieder 4000000. Dann wird der Timer in 1 Sekunde 4000000 / 1024 = 3906.25 mal erhöht. Da der Timer wieder jedesmal bis 255 zählen muss bis ein Overflow auftritt, bedeutet dies, dass in 1 Sekunde 3906.25 / 256 = 15.25 Overflows auftreten.&lt;br /&gt;
&lt;br /&gt;
Systemtakt: 4Mhz&lt;br /&gt;
&lt;br /&gt;
  Vorteiler    Overflows/Sekunde      Zeit zwischen&lt;br /&gt;
                                      2 Overflows [s]&lt;br /&gt;
&lt;br /&gt;
      1            15625                0.000064&lt;br /&gt;
      8             1953.125            0.000512&lt;br /&gt;
     64              244.1406           0.004096&lt;br /&gt;
    256               61.0351           0.016384&lt;br /&gt;
   1024               15.2587           0.065536&lt;br /&gt;
&lt;br /&gt;
==Erste Tests==&lt;br /&gt;
&lt;br /&gt;
Ein Programm das einen Timer Overflow in Aktion zeigt könnte z.B. so aussehen:&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 leds = r17&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, 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, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     leds, 0xFF&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, 0b00000001      ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&lt;br /&gt;
        sei&lt;br /&gt;
&lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
timer0_overflow:                      ; Timer 0 Overflow Handler&lt;br /&gt;
        out     PORTB, leds&lt;br /&gt;
        com     leds&lt;br /&gt;
        reti&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm beginnt mit der [[AVR-Tutorial:_Interrupts|Interrupt-Vektoren-Tabelle]]. Dort ist an der Adresse &amp;lt;i&amp;gt;OVF0Addr&amp;lt;/i&amp;gt; ein Sprung zur Marke &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt; eingetragen. Wenn also ein Overflow Interrupt vom Timer 0 auftritt, so wird dieser Interrupt durch den &#039;&#039;&#039;rjmp&#039;&#039;&#039; weitergeleitet an die Stelle &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Hauptprogramm beginnt ganz normal mit der Belegung des Stackpointers. Danach wird der Port B auf Ausgang geschaltet, wir wollen hier wieder die LED anschliessen.&lt;br /&gt;
&lt;br /&gt;
Durch Beschreiben von TCCR0 mit dem Bitmuster 0b00000001 wird der Vorteiler auf 1 gesetzt. Für die ersten Versuche empfiehlt es sich, das Programm mit dem AVR-Studio zunächst zu simulieren. Würden wir einen größeren Vorteiler benutzen, so müsste man ziemlich oft mittels F11 einen simulierten Schritt ausführen um eine Änderung im Timerregister zu erreichen.&lt;br /&gt;
&lt;br /&gt;
Die nächsten Anweisungen setzen im TIMSK Register das TOIE0 Bit. Sinn der Sache ist es, dem Timer zu erlauben bei Erreichen eines Overflow einen Interrupt auszulösen.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss noch die Interrupts generell mittels &#039;&#039;&#039;sei&#039;&#039;&#039; freigeben. Dieser Schritt ist obligatorisch. Im Mikrocontroller können viele Quellen einen Interrupt auslösen. Daraus folgt: Für jede mögliche Quelle muss festgelegt werden, ob sie einen Interrupt erzeugen darf oder nicht. Die Oberhoheit hat aber das globale Interrupt Flag. Mit ihm können alle Interrupts, egal von welcher Quelle sie kommen, unterdrückt werden.&lt;br /&gt;
&lt;br /&gt;
Damit ist die Initialisierung beendet und das Hauptprogramm kann sich schlafen legen. Die &#039;&#039;&#039;loop: rjmp loop&#039;&#039;&#039; Schleife macht genau dieses.&lt;br /&gt;
&lt;br /&gt;
Tritt nun ein Overflow am Timer auf, so wird über den Umweg über die Interrupt Vektor Tabelle der Programmteil &amp;lt;i&amp;gt;timer0_overflow&amp;lt;/i&amp;gt; angesprungen. Dieser gibt einfach nur den Inhalt des Registers leds am Port B aus. Danach wird das leds Register mit einer &#039;&#039;&#039;com&#039;&#039;&#039; Operation negiert, so dass aus allen 0 Bits eine 1 wird und umgekehrt. Die Overflow Behandlung ist damit beendet und mittels &#039;&#039;&#039;reti&#039;&#039;&#039; wird der Interrupt Handler wieder verlassen.&lt;br /&gt;
&lt;br /&gt;
==Simulation im AVR-Studio==&lt;br /&gt;
&lt;br /&gt;
Es lohnt sich den ganzen Vorgang im AVR-Studio simulieren zu lassen. Dazu am besten in der linken I/O View Ansicht die Einträge für PORTB und TIMER_COUNTER_0 öffnen. Wird mittels F11 durch das Programm gegangen, so sieht man, dass ab dem Moment, ab dem der Vorteiler auf 1 gesetzt wird, der Timer 0 im TCNT0 Register zu zählen anfängt. Mit jedem Druck auf F11 erhöht sich der Zählerstand. Irgendwann ist dann die Endlosschleife loop erreicht. Drücken Sie weiterhin F11 und beobachten sie, wie TCNT0 immer höher zählt, bis der Overflow erreicht wird. In dem Moment, in dem der Overflow erreicht wird, wird der Interrupt ausgelöst. Mit dem nächsten F11 landen sie in der Interrupt Vektor Tabelle und von dort geht es weiter zu timer_0_overflow. Weitere Tastendrücke von F11 erledigen dann die Ausgabe auf den Port B, das invertieren des Registers r17 und der Interrupt ist damit behandelt. Nach dem &#039;&#039;&#039;reti&#039;&#039;&#039; macht der Microcontroller genau an der Stelle weiter, an der er vom Interrupt unterbrochen wurde. Und der Timer 0 hat in der Zwischenzeit weitergezählt! Nach exakt weiteren 256 Schritten, vom Auftreten des ersten Overflows an gerechnet, wird der nächste Overflow ausgelöst.&lt;br /&gt;
&lt;br /&gt;
==Wie schnell schaltet denn jetzt der Port?==&lt;br /&gt;
&lt;br /&gt;
Eine berechtigte Frage. Dazu müssen wir etwas rechnen. Keine Angst, es ist nicht schwer, und wer das Prinzip bisher verstanden hat, der sollte keine Schwierigkeiten haben die Berechnung nachzuvollziehen.&lt;br /&gt;
&lt;br /&gt;
Der Quarzoszillator schwingt mit 4 MHz. Das heißt in 1 Sekunde werden 4000000 Taktzyklen generiert. Durch die Wahl des Vorteilers von 1 bedeutet das auch, dass der Timer 4000000 mal in der Sekunde erhöht wird. Von einem Overflow zum nächsten muss der Timer 256 Zählvorgänge ausführen. Also werden in 1 Sekunde 4000000 / 256 = 15625 Overflows generiert. Bei jedem Overflow schalten wir die LEDs jeweils in den anderen Zustand. D.h die LEDs blinken mit einer Frequenz von 7812.5 Hz. Das ist zuviel als dass wir es noch sehen könnten.&lt;br /&gt;
&lt;br /&gt;
Was können wir also tun um diese Blinkfrequenz zu verringern? Im Moment ist unsere einzige Einflussgröße der Vorteiler. Wie sieht die Rechnung aus, wenn wir einen Vorteiler von 1024 wählen?&lt;br /&gt;
&lt;br /&gt;
Wiederrum: Der Systemtakt sei 4 Mhz. Durch den Vorteiler von 1024 werden daraus 4000000 / 1024 = 3906.25 Pulse pro Sekunde für den Timer. Der zählt wiederum 256 Zustände von einem Overflow zum nächsten. 3906.25 / 256 = 15.2587. Und wiederum: Im Overflow werden die LEDs ja abwechselnd ein und ausgeschaltet, also dividieren wir noch durch 2: 15.2587 / 2 = 7.629. Also knapp 7 Hz. Diese Frequenz müsste man schon mit freiem Auge sehen. Die LEDs werden ziemlich schnell vor sich hin blinken.&lt;br /&gt;
&lt;br /&gt;
Reicht diese Verzögerung noch immer nicht, dann haben wir 2 Möglichkeiten:&lt;br /&gt;
* Entweder wir benutzen einen anderen Timer. Timer 1 beispielsweise ist ein 16 Bit Timer. Der Timer zählt also nicht bis 256 sondern bis 65536. Bei entsprechender Umarbeitung des Programms und einem Vorteiler von 1024 bedeutet das, dass die LEDs einen Ein/Aus Zyklus in 33 Sekunden absolvieren.&lt;br /&gt;
* Oder wir schalten die LEDs nicht bei jedem Timer Overflow um. Man könnte zum Beispiel in einem Register bis 7 zählen und nur dann, wenn dieses Register 7 erreicht hat, wird&lt;br /&gt;
** das Register wieder auf 0 gesetzt und&lt;br /&gt;
** die LEDs umgeschaltet.&lt;br /&gt;
&lt;br /&gt;
==Timer 0==&lt;br /&gt;
&lt;br /&gt;
Timer 0 ist ein 8 Bit Timer.&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
&lt;br /&gt;
===TCCR0===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      | CS02 | CS01 | CS00 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS02 - CS00   Clock Select&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;CS02&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;CS01&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;CS00&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;keine (Der Timer ist angehalten)&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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:    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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:    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 align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:   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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  256&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler: 1024&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Externer Takt vom Pin T0, fallende Flanke&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 align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Externer Takt vom Pin T0, steigende Flanke&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;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      | TOIE0|&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE0         Timer 0 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Ist dieses Bit gesetzt, so wird beim Auftreten eines&lt;br /&gt;
    Overflows am Timer ein Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
Anstatt der Schreibweise&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
ist es besser, die Schreibweise&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi     temp, 1 &amp;lt;&amp;lt; TOIE0&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu wählen, da hier unmittelbar aus dem Ladekommando hervorgeht, welche Bedeutung das gesetzte Bit hat. Die vorher inkludierte m8def.inc definiert dazu alles Notwendige.&lt;br /&gt;
&lt;br /&gt;
==Timer 1==&lt;br /&gt;
&lt;br /&gt;
Timer 1 ist ein 16 Bit Timer&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
* Clear Timer on Compare Match&lt;br /&gt;
* Input Capture&lt;br /&gt;
* 2 Compare Einheiten&lt;br /&gt;
* div. PWM Modi&lt;br /&gt;
&lt;br /&gt;
===TCCR1B===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | ICNC1| ICES1|      | WGM13| WGM12| CS12 | CS11 | CS10 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS12 - CS10   Clock Select&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&amp;gt;CS12&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS11&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS10&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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;keine (Der Timer ist angehalten)&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;Vorteiler:    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;&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;Vorteiler:    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;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;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  256&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;Vorteiler: 1024&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;Externer Takt vom Pin T1, fallende Flanke&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;Externer Takt vom Pin T1, steigende Flanke&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;
  &amp;lt;i&amp;gt;ICES1   Input Capture Edge Select&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;ICNC1   Input Capture Noise Canceler&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TCCR1A===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |COM1A1|COM1A0|COM1B1|COM1B0|FOC1A |FOC1B |WGM11 |WGM10 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===OCR1A===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===OCR1B===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===ICR1===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |TICIE1|OCIE1A|OCIE1B| TOIE1|      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TICIE1    Timer 1 Input Capture Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE1A    Timer 1 Output Compare A Match Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE1B    Timer 1 Output Compare B Match Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE1     Timer 1 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Timer 2==&lt;br /&gt;
Timer 2 ist ein 8 Bit Timer.&lt;br /&gt;
&lt;br /&gt;
* Overflow Interrupt&lt;br /&gt;
* Compare Match Interrupt&lt;br /&gt;
* Clear Timer on Compare Match&lt;br /&gt;
* Phasen korrekte PWM&lt;br /&gt;
&lt;br /&gt;
===TCCR2===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | FOC2 | WGM20| COM21| COM20| WGM21| CS22 | CS21 | CS20 |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;CS22 - CS20   Clock Select&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&amp;gt;CS22&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS21&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CS20&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Bedeutung&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;keine (Der Timer ist angehalten)&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;Vorteiler:    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;&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;Vorteiler:    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;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;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:   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;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vorteiler:  128&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;Vorteiler:  256&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;Vorteiler: 1024&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;
===OCR2===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  |      |      |      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
===TIMSK===&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
  | OCIE2| TOIE2|      |      |      |      |      |      |&lt;br /&gt;
  +------+------+------+------+------+------+------+------+&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;OCIE2   Timer 2 Output Compare Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;i&amp;gt;TOIE2   Timer 2 Overflow Interrupt Enable&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Was geht noch mit einem Timer?==&lt;br /&gt;
&lt;br /&gt;
Timer sind sehr universelle Microcontroller Bestandteile. Für weitergehende Studien ist es daher unerlässlich das entsprechende Datenblatt des Microcontrollers zu studieren. Oft ist es zb. möglich, dass der Timer bei erreichen von bestimmten Zählerständen einen Ausgabepin von sich aus ein-/aus-/umschaltet. Er erledigt dann das was wir oben noch mit einem Interrupt gemacht haben eigenständig komplett in Hardware. Bei einigen Timern ist es möglich damit eine [[PWM]] (Pulsweiten-Modulation) aufzubauen.&lt;br /&gt;
&lt;br /&gt;
Ein paar der Timermodule lassen sich auch als Counter verwenden. Damit kann man z.B. die Anzahl externer Ereignisse wie Schaltvorgänge eines Inkrementalgebers bestimmen.&lt;br /&gt;
&lt;br /&gt;
Andere bieten die Möglichkeit über einen externen Uhrenquarz getaktet zu werden. (Anwendung z.B. eine &amp;quot;Echtzeituhr&amp;quot; oder als &amp;quot;Weckfunktion&amp;quot; aus einem&lt;br /&gt;
Standby/Powerdownmodus)&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* Timer/Counter und PWM beim ATMega16 Mikrocontroller: http://www.uni-koblenz.de/~physik/informatik/MCU/Timer.pdf &lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Speicher|&lt;br /&gt;
zurücklink=AVR-Tutorial: Speicher|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Uhr|&lt;br /&gt;
vorlink=AVR-Tutorial: Uhr}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=28001</id>
		<title>AVR-Tutorial: LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=28001"/>
		<updated>2008-05-16T03:40:04Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Ausgabe eines konstanten Textes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kaum ein elektronisches Gerät kommt heutzutage noch ohne ein LCD daher. Ist doch auch praktisch, Informationen im Klartext anzeigen zu können, ohne irgendwelche LEDs blinken zu lassen. Kein Wunder also, dass die häufigste Frage in Mikrocontroller-Foren ist: &amp;quot;Wie kann ich ein LCD anschließen?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-Bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-Bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-Bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-Bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-Bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-Bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4 Bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert. Ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitet hat. Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt, da wir wissen, welche Anschlüsse das LCD benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Pinnummer&amp;lt;BR&amp;gt;LCD || Bezeichnung || Anschluss&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |1 || Vss || GND&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |2 || Vcc || 5V&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |3 || Vee || GND oder [[Potentiometer | Poti]] &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |4 || RS || PD4 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |5 || RW || GND &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |6 || E || PD5 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |7 || DB0 || nicht angeschlossen &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |8 || DB1 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |9 || DB2 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |10 || DB3 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |11 || DB4 || PD0 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |12 || DB5 || PD1 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |13 || DB6 || PD2 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |14 || DB7 || PD3 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |15 || A || Vorsicht! Meistens nicht direkt an +5V anschließbar,&amp;lt;BR&amp;gt;nur über einen Vorwiderstand (z.B. 33&amp;amp;Omega;)!&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |16 || K || GND&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Ok alles ist verbunden. Wenn man jetzt den Strom einschaltet, sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display?&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung des LCDs im 4-Bit-Modus ==&lt;br /&gt;
&lt;br /&gt;
Um ein Byte zu übertragen, muss man es erstmal in die beiden Nibbles zerlegen, die getrennt übertragen werden. Da das obere Nibble (Bit 4 - Bit 7) als erstes übertragen wird, die 4 Datenleitungen jedoch an die vier unteren Bits des Port D angeschlossen sind, muss man die beiden Nibbles des zu übertragenden Bytes erstmal vertauschen. Der AVR kennt dazu praktischerweise einen eigenen Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           swap r16               ; vertauscht die beiden Nibbles von r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus 0b00100101 wird so z.B. 0b01010010. &lt;br /&gt;
&lt;br /&gt;
Jetzt sind die Bits für die erste Phase der Übertragung an der richtigen Stelle. Trotzdem wollen wir das Ergebnis nicht einfach so mit &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; an den Port geben. Um die Hälfte des Bytes, die jetzt nicht an die Datenleitungen des LCDs gegeben wird auf null zu setzen, verwendet man folgenden Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           andi r16, 0b00001111   ; Nur die vier unteren (mit 1 markierten)&lt;br /&gt;
                                  ; Bits werden übernommen, alle anderen werden null&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also: Das obere Nibble wird erst mit dem unteren vertauscht, damit es unten ist. Dann wird das obere (das wir jetzt noch nicht brauchen) auf null gesetzt. &lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem LCD noch mitteilen, ob wir Daten oder Befehle senden wollen. Das machen wir, indem wir das Bit, an dem RS angeschlossen ist (PD4), auf 0 (Befehl senden) oder auf 1 (Daten senden) setzen. Um ein Bit in einem normalen Register zu setzen, gibt es den Befehl sbr (Set Bit in Register). Dieser Befehl unterscheidet sich jedoch von sbi (das nur für IO-Register gilt) dadurch, dass man nicht die Nummer des zu setzenden Bits angibt, sondern eine Bitmaske. Das geht so: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbr r16, 0b00010000     ; Bit 4 setzen, alle anderen Bits bleiben gleich&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RS ist an PD4 angeschlossen. Wenn wir r16 an den Port D ausgeben, ist RS jetzt also high und das LCD erwartet Daten anstatt von Befehlen. &lt;br /&gt;
&lt;br /&gt;
Das Ergebnis können wir jetzt endlich direkt an den Port D übergeben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           out PORTD, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss vorher der Port D auf Ausgang geschalten werden, indem man 0xFF ins Datenrichtungsregister DDRD schreibt. &lt;br /&gt;
&lt;br /&gt;
Um dem LCD zu signalisieren, dass es das an den Datenleitungen anliegende Nibble übernehmen kann, wird die E-Leitung (Enable, an PD5 angeschlossen) auf high und kurz darauf wieder auf low gesetzt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbi PORTD, 5              ; Enable high&lt;br /&gt;
           nop                       ; 3 Taktzyklen warten (&amp;quot;nop&amp;quot; = nichts tun)&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5              ; Enable wieder low&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die eine Hälfte des Bytes wäre damit geschafft! Die andere Hälfte kommt direkt hinterher: Alles, was an der obenstehenden Vorgehensweise geändert werden muss, ist, das &amp;quot;swap&amp;quot; (Vertauschen der beiden Nibbles) wegzulassen.&lt;br /&gt;
&lt;br /&gt;
== Initialisierung des Displays ==&lt;br /&gt;
&lt;br /&gt;
Allerdings gibt es noch ein Problem. Wenn ein LCD eingeschaltet wird, dann läuft es zunächst im 8 Bit Modus. Irgendwie muss das Display initialisiert und auf den 4 Bit Modus umgeschaltet werden, und zwar nur mit den 4 zur Verfügung stehenden Datenleitungen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn es Probleme gibt, dann meistens an diesem Punkt. Die &amp;quot;kompatiblen&amp;quot; Kontroller sind gelegentlich doch nicht 100% identisch. Es lohnt sich, das Datenblatt (siehe Weblinks im Artikel [[LCD]]) genau zu lesen, in welcher Reihenfolge und mit welchen Abständen (Delays) die Initialisierungbefehle gesendet werden. Eine weitere Hilfe können Ansteuerungsbeispiele in Forenbeiträgen geben z.B.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79609#664268 (A) KS0066U oder Ähnliche --- LCD Treiber]&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 4 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Achtung: Im Folgenden sind alle Bytes aus Sicht des LCD-Kontrollers angegeben! Da LCD-seitig nur die Leitungen DB4 - DB7 verwendet werden, ist daher immer nur das höherwertige Nibble gültig. Durch die Art der Verschaltung (DB4 - DB7 wurde auf dem PORT an PD0 bis PD3 angeschlossen) ergibt sich eine Verschiebung, so dass das am Kontroller auszugebende Byte nibblemässig vertauscht ist!&lt;br /&gt;
&lt;br /&gt;
Die Sequenz, aus Sicht des Kontrollers, sieht so aus:&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* $2 ins Steuerregister schreiben (RS = 0), dadurch wird auf 4 Bit Daten umstellt&lt;br /&gt;
* Ab jetzt muss für die Übertragung eines Bytes jeweils zuerst das höherwertige Nibble und dann das niederwertige Nibble übertragen werden, wie oben beschrieben&lt;br /&gt;
* Mit dem Konfigurier-Befehl $20 das Display konfigurieren (4-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 8 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Der Vollständigkeit halber hier noch die notwendige Initialiserungssequenz für den 8 Bit Modus. Da hier die Daten komplett als 1 Byte übertragen werden können, sind einige Klimmzüge wie im 4 Bit Modus nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mit dem Konfigurier-Befehl 0x30 das Display konfigurieren (8-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
== Routinen zur LCD-Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
Die Routinen zur Kommunikation mit dem LCD sehen also so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000 (Anm.1)&lt;br /&gt;
           out PORTD, temp1             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000&lt;br /&gt;
           out PORTD, temp2             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out PORTD, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
 ;&lt;br /&gt;
 ; Bei höherem Takt (&amp;gt;= 8 MHz) kann es notwendig sein, &lt;br /&gt;
 ; vor dem Enable High 1-2 Wartetakte (nop) einzufügen. &lt;br /&gt;
 ; Siehe dazu http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi PORTD, 5                 ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5                 ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
&lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi  temp3,50&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall  delay5ms&lt;br /&gt;
           dec  temp3&lt;br /&gt;
           brne powerupwait&lt;br /&gt;
           ldi temp1, 0b00000011        ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out PORTD, temp1             ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00000010        ; 4bit-Modus einstellen&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00101000        ; 4Bit / 2 Zeilen / 5x8&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00001100        ; Display ein / Cursor aus / kein Blinken&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00000100        ; inkrement / kein Scrollen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi temp1, 0b00000001   ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi temp1, 0b00000010   ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anm.1: Siehe [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
Weitere Funktionen (wie z.B. Cursorposition verändern) sollten mit Hilfe der [[AVR-Tutorial:_LCD#Welche_Befehle_versteht_das_LCD.3F|Befehlscodeliste]] nicht schwer zu realisieren sein. Einfach den Code in temp laden, lcd_command aufrufen und ggf. eine Pause einfügen. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man die LCD-Ansteuerung auch an einen anderen Port des Mikrocontrollers &amp;quot;verschieben&amp;quot;: Wenn das LCD z.B. an Port B angeschlossen ist, dann reicht es, im Programm alle &amp;quot;PORTD&amp;quot; durch &amp;quot;PORTB&amp;quot; und &amp;quot;DDRD&amp;quot; durch &amp;quot;DDRB&amp;quot; zu ersetzen. &lt;br /&gt;
&lt;br /&gt;
Wer eine höhere Taktfrequenz als 4 MHz verwendet, der sollte daran denken, die Dauer der Verzögerungsschleifen anzupassen.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das diese Routinen zur Anzeige von Text verwendet, kann z.B. so aussehen (die Datei lcd-routines.asm muss sich im gleichen Verzeichnis befinden). Nach der Initialisierung wird zuerst der Displayinhalt gelöscht. Um dem LCD ein Zeichen zu schicken, lädt man es in temp1 und ruft die Routine &amp;quot;lcd_data&amp;quot; auf. Das folgende Beispiel zeigt das Wort &amp;quot;Test&amp;quot; auf dem LCD an. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/lcd-test.asm Download lcd-test.asm] &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;
&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, 0xFF    ; Port D = Ausgang&lt;br /&gt;
           out DDRD, temp1&lt;br /&gt;
&lt;br /&gt;
           rcall lcd_init     ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear    ; Display löschen&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;T&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;e&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           &lt;br /&gt;
           ldi temp1, &#039;s&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;t&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD-Routinen werden hier eingefügt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für längere Texte ist die Methode, jedes Zeichen einzeln in das Register zu laden und &amp;quot;lcd_data&amp;quot; aufzurufen natürlich nicht sehr praktisch. Dazu später aber mehr.&lt;br /&gt;
&lt;br /&gt;
Bisher wurden in Register immer irgendwelche Zahlenwerte geladen, aber in diesem Programm kommt plötzlich die Anweisung&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
vor. Wie ist diese zu verstehen? Passiert hier etwas grundlegend anderes als beim Laden einer Zahl in ein Register?&lt;br /&gt;
&lt;br /&gt;
Die Antwort darauf lautet: Nein. Auch hier wird letztendlich nur eine Zahl in ein Register geladen. Der Schlüssel zum Verständnis beruht darauf, dass zum LCD, so wie zu allen Ausgabegeräten, für die Ausgabe von Texten immer nur Zahlen übertragen werden, sog. Codes. Zum Beispiel könnte man vereinbaren, dass ein LCD, wenn es den Ausgabecode 65 erhält, ein &#039;A&#039; anzeigt, bei einem Ausgabecode von 66 ein &#039;B&#039; usw. Naturgemäß gibt es daher viele verschiedene Code-Buchstaben Zuordnungen. Damit hier etwas Ordnung in das potentielle Chaos kommt, hat man sich bereits in der Steinzeit der Programmierung auf bestimmte Codetabellen geeinigt, von denen die verbreitetste sicherlich die ASCII-Zuordnung ist.&lt;br /&gt;
&lt;br /&gt;
==ASCII==&lt;br /&gt;
&lt;br /&gt;
ASCII steht für &#039;&#039;American Standard Code for Information Interchange&#039;&#039; und ist ein standardisierter Code zur Zeichenumsetzung. Die Codetabelle sieht hexadezimal dabei wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;.0&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x1&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x2&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x3&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x4&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x6&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x7&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x8&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x9&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xA&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xB&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xC&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xD&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xE&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xF&amp;lt;/th&amp;gt;&amp;lt;br&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;&#039;&#039;&#039;0x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NUL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SOH&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;STX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EOT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ENQ&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ACK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BEL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;HT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;VT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CR&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SO&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SI&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;&#039;&#039;&#039;1x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DLE&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NAK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SYN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CAN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EM&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SUB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ESC&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;US&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;&#039;&#039;&#039;2x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SP&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;!&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;#&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;%&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;amp;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;(&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;)&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;*&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;+&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;,&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;.&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;3x&#039;&#039;&#039;&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;&amp;lt;td&amp;gt;3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;9&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;=&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;gt;&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;4x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;B&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;C&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;D&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;F&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;G&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;H&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;I&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;J&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;L&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;M&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;N&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;O&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;&#039;&#039;&#039;5x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;P&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;R&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;S&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;T&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;U&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;V&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;W&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;X&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;[&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;\&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;]&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;^&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;6x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;`&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;a&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;b&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;c&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;d&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;e&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;f&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;g&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;h&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;i&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;j&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;k&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;l&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;m&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;n&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;o&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;&#039;&#039;&#039;7x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;p&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;r&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;s&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;t&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;u&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;v&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;w&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;{&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;|&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;~&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DEL&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 ersten beiden Zeilen enthalten die Codes für einige Steuerzeichen, ihre vollständige Beschreibung würde hier zu weit führen. Das Zeichen &#039;&#039;&#039;SP&#039;&#039;&#039; steht für ein &#039;&#039;Space&#039;&#039;, also ein Leerzeichen. &#039;&#039;&#039;BS&#039;&#039;&#039; steht für &#039;&#039;Backspace&#039;&#039;, also ein Zeichen zurück. &#039;&#039;&#039;DEL&#039;&#039;&#039; steht für &#039;&#039;Delete&#039;&#039;, also das Löschen eines Zeichens. &#039;&#039;&#039;CR&#039;&#039;&#039; steht für &#039;&#039;Carriage Return&#039;&#039;, also wörtlich: der Wagenrücklauf (einer Schreibmaschine), während &#039;&#039;&#039;LF&#039;&#039;&#039; für &#039;&#039;Line feed&#039;&#039;, also einen Zeilenvorschub steht.&lt;br /&gt;
&lt;br /&gt;
Der Assembler kennt diese Codetabelle und ersetzt die Zeile&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, $54&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
was letztendlich auch der Lesbarkeit des Programmes zugute kommt. Funktional besteht kein Unterschied zwischen den beiden Anweisungen. Beide bewirken, dass das Register temp1 mit dem Bitmuster 01010100 ( = hexadezimal 54, = dezimal 84 oder eben der ASCII Code für &#039;&#039;&#039;T&#039;&#039;&#039;) geladen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das LCD wiederrum kennt diese Code-Tabelle ebenfalls und wenn es über den Datenbus die Codezahl $54 zur Anzeige empfängt, dann schreibt es ein &#039;&#039;&#039;T&#039;&#039;&#039; an die aktuelle Cursorposition. Genauer gesagt, weiss das LCD nichts von einem &#039;&#039;&#039;T&#039;&#039;&#039;. Es sieht einfach in seinen internen Tabellen nach, welche Pixel beim Empfang der Codezahl $54 auf schwarz zu setzen sind. &#039;Zufällig&#039; sind das genau jene Pixel, die für uns Menschen ein &#039;&#039;&#039;T&#039;&#039;&#039; ergeben.&lt;br /&gt;
&lt;br /&gt;
==Welche Befehle versteht das LCD?==&lt;br /&gt;
&lt;br /&gt;
Auf dem LCD arbeitet ein Kontroller vom Typ HD44780. Dieser Kontroller versteht eine Reihe von Befehlen, die allesamt mittels lcd_command gesendet werden können. Ein Kommando ist dabei nichts anderes als ein Befehlsbyte, in dem die verschiedenen Bits verschiedene Bedeutungen haben:&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 0 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 1 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;der Zustand dieses Bits ist egal&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;sonstige Buchstaben&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;das Bit muss je nach gewünschter Funktionalität gesetzt werden. Die mögliche Funktionalität des jeweiligen Bits geht aus der Befehlsbeschreibung hervor&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Kommando &#039;ON/OFF Control&#039; soll benutzt werden, um das Display einzuschalten, der Cursor soll eingeschaltet werden und der Cursor soll blinken.&lt;br /&gt;
Das Befehlsbyte ist so aufgebaut:&lt;br /&gt;
   0b00001dcb&lt;br /&gt;
Aus der Befehlsbeschreibung entnimmt man:&lt;br /&gt;
* Display ein bedeutet, dass an der Bitposition d eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor ein bedeutet, dass an der Bitposition c ein 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor blinken bedeutet, dass an der Bitposition b eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
Das dafür zu übertragende Befehlsbyte hat also die Gestalt 0b00001111 oder in hexadezimaler Schreibweise $0F.&lt;br /&gt;
&lt;br /&gt;
===Clear display: 0b00000001===&lt;br /&gt;
&lt;br /&gt;
Die Anzeige wird gelöscht und der Ausgabecursor kehrt an die Home Position (links, erste Zeile) zurück.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Cursor home: 0b0000001x===&lt;br /&gt;
&lt;br /&gt;
Der Cursor kehrt an die Home Position (links, erste Zeile) zurück. Ein verschobenes Display wird auf die Grundeinstellung zurückgesetzt.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs bis 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Entry mode: 0b000001is===&lt;br /&gt;
&lt;br /&gt;
Legt die Cursor Richtung sowie eine mögliche Verschiebung des Displays fest&lt;br /&gt;
* i = 1, Cursorposition bei Ausgabe eines Zeichens erhöhen&lt;br /&gt;
* i = 0, Cursorposition bei Ausgabe eines Zeichens vermindern&lt;br /&gt;
* s = 1, Display wird gescrollt, wenn der Cursor das Ende/Anfang, je nach Einstellung von i, erreicht hat.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===On/off control: 0b00001dcb===&lt;br /&gt;
&lt;br /&gt;
Display insgesamt ein/ausschalten; den Cursor ein/ausschalten; den Cursor auf blinken schalten/blinken aus. Wenn das Display ausgeschaltet wird, geht der Inhalt des Displays nicht verloren. Der vorher angezeigte Text wird nach wiedereinschalten erneut angezeigt.&lt;br /&gt;
Ist der Cursor eingeschaltet, aber Blinken ausgeschaltet, so wird der Cursor als Cursorzeile in Pixelzeile 8 dargestellt. Ist Blinken eingeschaltet, wird der Cursor als blinkendes ausgefülltes Rechteck dargestellt, welches abwechselnd mit dem Buchstaben an dieser Stelle angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
* d = 0, Display aus&lt;br /&gt;
* d = 1, Display ein&lt;br /&gt;
* c = 0, Cursor aus&lt;br /&gt;
* c = 1, Cursor ein&lt;br /&gt;
* b = 0, Cursor blinken aus&lt;br /&gt;
* b = 1, Cursor blinken ein&lt;br /&gt;
 &lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Cursor/Scrollen: 0b0001srxx===&lt;br /&gt;
&lt;br /&gt;
Bewegt den Cursor oder scrollt das Display um eine Position entweder nach rechts oder nach links.&lt;br /&gt;
&lt;br /&gt;
* s = 1, Display scrollen&lt;br /&gt;
* s = 0, Cursor bewegen&lt;br /&gt;
* r = 1, nach rechts&lt;br /&gt;
* r = 0, nach links &lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Konfiguration: 0b001dnfxx===&lt;br /&gt;
&lt;br /&gt;
Einstellen der Interface Art, Modus, Font&lt;br /&gt;
* d = 0, 4-Bit Interface&lt;br /&gt;
* d = 1, 8-Bit Interface&lt;br /&gt;
* n = 0, 1 zeilig&lt;br /&gt;
* n = 1, 2 zeilig&lt;br /&gt;
* f = 0, 5x7 Pixel&lt;br /&gt;
* f = 1, 5x11 Pixel&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Character RAM Address Set: 0b01aaaaaa===&lt;br /&gt;
&lt;br /&gt;
Mit diesem Kommando werden maximal 8 selbst definierte Zeichen definiert. Dazu wird der Character RAM Zeiger auf den Anfang des Character Generator (CG) RAM gesetzt und das Zeichen durch die Ausgabe von 8 Byte definiert. Der Adresszeiger wird nach Ausgabe jeder Pixelzeile (8 Bit) vom LCD selbst erhöht. Nach Beendigung der Zeichendefinition muss die Schreibposition explizit mit dem Kommando &amp;quot;Display RAM Address Set&amp;quot; wieder in den DD-RAM Bereich gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
aaaaaa 6-bit CG RAM Adresse&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Display RAM Address Set: 0b1aaaaaaa===&lt;br /&gt;
&lt;br /&gt;
Den Cursor neu positionieren. Display Data (DD) Ram ist vom Character Generator (CG) Ram unabhängig. Der Adresszeiger wird bei Ausgabe eines Zeichens ins DD Ram automatisch erhöht. Das Display verhält sich so, als ob eine Zeile immer aus 32 logischen Zeichen besteht, von der, je nach konkretem Displaytyp (16 Zeichen, 20 Zeichen) immer nur ein Teil sichtbar ist.&lt;br /&gt;
&lt;br /&gt;
aaaaaaa 7-bit DD RAM Adresse. Auf 2-zeiligen Displays (und den meisten 16x1 Displays), kann die Adressangabe wie folgt interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
1laaaaaa&lt;br /&gt;
* l = Zeilennummer (0 oder 1)&lt;br /&gt;
* a = 6-Bit Spaltennummer&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
==Einschub: Code aufräumen==&lt;br /&gt;
&lt;br /&gt;
Es wird Zeit, sich einmal etwas kritisch mit den bisher geschriebenen Funktionen auseinander zu setzen.&lt;br /&gt;
&lt;br /&gt;
===Portnamen aus dem Code herausziehen===&lt;br /&gt;
&lt;br /&gt;
Wenn wir die LCD-Funktionen einmal genauer betrachten, dann fällt sofort auf, dass über die Funktionen verstreut immer wieder das &#039;&#039;&#039;PORTD&#039;&#039;&#039; sowie einzelne Zahlen für die Pins an diesem Port auftauchen. Wenn das LCD an einem anderen Port betrieben werden soll, oder sich die Pin-Belegung ändert, dann muss an all diesen Stellen eine Anpassung vorgenommen werden. Dabei darf keine einzige Stelle übersehen werden, ansonsten würden die LCD-Funktionen nicht oder nicht vollständig funktionieren.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit, dem vorzubeugen, ist es, diese immer gleichbleibenden Dinge an den Anfang der LCD-Funktionen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
.equ PIN_E    = 5&lt;br /&gt;
.equ PIN_RS   = 4&lt;br /&gt;
&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp1          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp2          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi LCD_PORT, PIN_E          ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi LCD_PORT, PIN_E          ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
 &lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf Ausgang&lt;br /&gt;
           out   LCD_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi   temp3,6&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           dec   temp3&lt;br /&gt;
           brne  powerupwait&lt;br /&gt;
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out   LCD_PORT, temp1        ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00101000      ; 4 Bot, 2 Zeilen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00001100      ; Display on, Cursor off&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00000100      ; endlich fertig&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi   temp1, 0b00000010      ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels &#039;&#039;&#039;.equ&#039;&#039;&#039; werden mit dem Assembler Textersetzungen vereinbart. Der Assembler ersetzt alle Vorkomnisse des Quelltextes durch den zu ersetzenden Text. Dadurch ist es z.B. möglich, alle Vorkommnisse von &#039;&#039;&#039;PORTD&#039;&#039;&#039; durch &#039;&#039;&#039;LCD_PORT&#039;&#039;&#039; auszutauschen. Wird das LCD an einen anderen Port, z.B. &#039;&#039;&#039;PORTB&#039;&#039;&#039; gelegt, dann genügt es, die Zeilen&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTB&lt;br /&gt;
.equ LCD_DDR  = DDRB&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu ersetzen. Der Assembler sorgt dann dafür, dass diese Portänderung an den relevanten Stellen im Code über die Textersetzungen einfließt. Selbiges natürlich mit der Pin-Zuordnung.&lt;br /&gt;
&lt;br /&gt;
===Registerbenutzung===&lt;br /&gt;
&lt;br /&gt;
Bei diesen Funktionen mussten einige Register des Prozessors benutzt werden, um darin Zwischenergebnisse zu speichern bzw. zu bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss dabei natürlich, dass es zu keinen Überschneidungen kommt. Solange nur jede Funktion jeweils für sich betrachtet wird, ist das kein Problem. In 20 oder 30 Code-Zeilen kann man gut verfolgen, welches Register wofür benutzt wird. Schwieriger wird es, wenn Funktionen wiederum andere Funktionen aufrufen, die ihrerseits wieder Funktionen aufrufen usw. Jede dieser Funktionen benutzt einige Register und mit zunehmender Programmgröße wird es immer schwieriger, zu verfolgen, welches Register zu welchem Zeitpunkt wofür benutzt wird.&lt;br /&gt;
&lt;br /&gt;
Speziell bei Basisfunktionen wie diesen LCD-Funktionen, ist es daher oft ratsam, dafür zu sorgen, dass jede Funktion die Register wieder in dem Zustand hinterlässt, indem sie sie auch vorgefunden hat. Wir benötigen dazu wieder den Stack, auf dem die Registerinhalte bei Betreten einer Funktion zwischengespeichert werden und von dem die Register bei Verlassen einer Funktion wiederhergestellt werden.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir die Funktion&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion verändert das Register temp1. Um das Register abzusichern, schreiben wir die Funktion um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           push  temp1                  ; temp1 auf dem Stack sichern&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           pop   temp1                  ; temp1 vom Stack wiederherstellen&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am besten hält man sich an die Regel: Jede Funktion ist dafür zuständig, die Register zu sichern und wieder herzustellen, die sie auch selbst verändert. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; ruft die Funktionen &#039;&#039;&#039;lcd_command&#039;&#039;&#039; und &#039;&#039;&#039;delay5ms&#039;&#039;&#039; auf. Wenn diese Funktionen selbst wieder Register verändern (und das tun sie), so ist es die Aufgabe dieser Funktionen, sich um die Sicherung und das Wiederherstellen der entsprechenden Register zu kümmern. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; sollte sich nicht darum kümmern müssen. Auf diese Weise ist das Schlimmste, das einem passieren kann, dass ein paar Register unnütz gesichert und wiederhergestellt werden. Das kostet zwar etwas Rechenzeit und etwas Speicherplatz auf dem Stack, ist aber immer noch besser als das andere Extrem: Nach einem Funktionsaufruf haben einige Register nicht mehr den Wert, den sie haben sollten, und das Programm rechnet mit falschen Zahlen weiter.&lt;br /&gt;
&lt;br /&gt;
===Lass den Assembler rechnen===&lt;br /&gt;
Betrachtet man den Code genauer, so fallen einige konstante Zahlenwerte auf (Das vorangestellte $ kennzeichnet die Zahl als Hexadezimalzahl):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Code benötigt eine Warteschleife, die mindestens 50µs dauert. Die beiden Befehle innerhalb der Schleife benötigen 3 Takte: 1 Takt für den &#039;&#039;&#039;dec&#039;&#039;&#039; und der &#039;&#039;&#039;brne&#039;&#039;&#039; benötigt 2 Takte, wenn die Bedingung zutrifft, der Branch also genommen wird. Bei 4 Mhz werden also 4000000 / 3 * 50 / 1000000 = 66.6 Durchläufe durch die Schleife benötigt, um eine Verzögerungszeit von 50µs (0.000050 Sekunden) zu erreichen, hexadezimal ausgedrückt: $42.&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist: Bei anderen Taktfrequenzen müsste man nun jedesmal diese Berechnung machen und den entsprechenden Zahlenwert einsetzen. Das kann aber der Assembler genausogut erledigen. Am Anfang des Codes wird ein Eintrag definiert, der die Taktfrequenz festlegt. Traditionell heißt dieser Eintrag &amp;lt;i&amp;gt;XTAL&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ XTAL  = 4000000&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 50 / 3 ) / 1000000&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An einer anderen Codestelle gibt es weitere derartige magische Zahlen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was geht hier vor?&lt;br /&gt;
Die innere Schleife benötigt wieder 3 Takte pro Durchlauf. Bei $C9 = 201 Durchläufen werden also 201 * 3 = 603 Takte verbraucht. In der äußeren Schleife werden pro Durchlauf alo 1 + 603 + 1 + 2 = 607 Takte verbraucht. Da die äußere Schleife $21 = 33 mal wiederholt wird, werden 20031 Takte verbraucht. Bei 4Mhz benötigt der Prozessor 20031 / 4000000 = 0.005007 Sekunden, also 5 ms.&lt;br /&gt;
Wird der Wiederholwert für die innere Schleife bei $C9 belassen, so werden 4000000 / 607 * 5 / 1000 Wiederholungen der äusseren Schleife benötigt. Auch diese Berechnung kann wieder der Assembler übernehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 5 / 607 ) / 1000&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein kleines Problem kann bei der Verwendung dieses Verfahrens entstehen: Bei hohen Taktfrequenzen und großen Wartezeiten kann der berechnete Wert größer als 255 werden und man bekommt die Fehlermeldung &amp;quot;Operand(s) out of range&amp;quot; beim Assemblieren. Dieser Fall tritt zum Beispiel für obige Konstruktion bei einer Taktfrequenz von 16 MHz ein (genauer gesagt ab 15,3 MHz), während darunter XTAL beliebig geändert werden kann. Als einfachste Lösung bietet es sich an, die Zahl der Takte pro Schleifendurchlauf durch das Einfügen von &#039;&#039;&#039;nop&#039;&#039;&#039; zu erhöhen und die Berechnungsvorschrift anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Ausgabe eines konstanten Textes ==&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde schon einmal ein Text ausgegeben. Dies geschah durch Ausgabe von einzelnen Zeichen. Das können wir auch anders machen. Wir können den Text im Speicher ablegen und eine Funktion schreiben, die die einzelnen Zeichen aus dem Speicher holt und ausgibt. Dabei erhebt sich aber eine Fragestellung: Woher weiß die Funktion eigentlich, wie lange der Text ist? Die Antwort darauf lautet: Sie kann es nicht wissen. Wir müssen irgendwelche Vereinbarungen treffen, woran die Funktion erkennen kann, dass der Text zu Ende ist. Im Wesentlichen werden dazu 2 Methoden benutzt:&lt;br /&gt;
* Der Text enthält ein spezielles Zeichen, welches das Ende des Textes markiert&lt;br /&gt;
* Wir speichern nicht nur den Text selbst, sondern auch die Länge des Textes&lt;br /&gt;
Mit einer der beiden Methoden ist es der Textausgabefunktion dann ein Leichtes, den Text vollständig auszugeben.&lt;br /&gt;
&lt;br /&gt;
Wir werden uns im Weiteren dafür entscheiden, ein spezielles Zeichen, eine 0, dafür zu benutzen. Die Ausgabefunktionen werden dann etwas einfacher, als wenn bei der Ausgabe die Anzahl der bereits ausgegebenen Zeichen mitgezählt werden muss.&lt;br /&gt;
&lt;br /&gt;
Den Text selbst speichern wir im Flash-Speicher, also dort, wo auch das Programm gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Einen konstanten Text aus dem Flash Speicher&lt;br /&gt;
 ; ausgeben. Der Text wird mit einer 0 beendet&lt;br /&gt;
lcd_flash_string:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_1:&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           rjmp  lcd_flash_string_1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_2:&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion benutzt den Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039;, um das jeweils nächste Zeichen aus dem Flash Speicher in ein Register zur Weiterverarbeitung zu laden. Dazu wird der sog. &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; benutzt. So nennt man das Registerpaar &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;. Nach jedem Ladevorgang wird dabei durch den Befehl&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
dieser Z-Pointer um 1 erhöht. Mittels &#039;&#039;&#039;cpi&#039;&#039;&#039; wird das in das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; geladene Zeichen mit 0 verglichen. &#039;&#039;&#039;cpi&#039;&#039;&#039; vergleicht die beiden Zahlen und merkt sich das Ergebnis in einem speziellen Register in Form von Status Bits. &#039;&#039;&#039;cpi&#039;&#039;&#039; zieht dabei ganz einfach die beiden Zahlen voneinander ab. Sind sie gleich, so kommt da als Ergebnis 0 heraus und &#039;&#039;&#039;cpi&#039;&#039;&#039; setzt daher konsequenter Weise das Zero-Flag, das anzeigt, dass die vorhergegangene Operation eine 0 als Ergebnis hatte.&#039;&#039;&#039;breq&#039;&#039;&#039; wertet diese Status-Bits aus. Wenn die vorhergegangene Operation ein 0-Ergebnis hatte, das Zero-Flag also gesetzt ist, dann wird ein Sprung zum angegebenen Label durchgeführt. In Summe bewirkt also die Sequenz&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
dass das gelesene Zeichen mit 0 verglichen wird und falls das gelesene&lt;br /&gt;
Zeichen tatsächlich 0 war, an der Stelle lcd_flash_string_2 weiter gemacht wird. Im anderen Fall wird die bereits geschriebene Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aufgerufen, welche das Zeichen ausgibt. &#039;&#039;&#039;lcd_data&#039;&#039;&#039; erwartet dabei das Zeichen im Register &#039;&#039;&#039;temp1&#039;&#039;&#039;, genau in dem Register, in welches wir vorher mittels &#039;&#039;&#039;lpm&#039;&#039;&#039; das Zeichen geladen hatten.&lt;br /&gt;
&lt;br /&gt;
Das verwendende Programm sieht dann 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 temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
           rcall lcd_init              ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear             ; Display löschen&lt;br /&gt;
 &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;
&lt;br /&gt;
           rcall lcd_flash_string      ; Unterprogramm gibt String aus der&lt;br /&gt;
                                       ; durch den Z-Pointer adressiert wird&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
           .db &amp;quot;Test&amp;quot;,0                ; Stringkonstante, durch eine 0&lt;br /&gt;
                                       ; abgeschlossen  &lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD Funktionen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Genaueres über die Verwendung unterschiedlicher Speicher findet sich im Kapitel [[AVR-Tutorial:_Speicher|Speicher]]&lt;br /&gt;
&lt;br /&gt;
==Zahlen ausgeben==&lt;br /&gt;
Um Zahlen, die beispielsweise in einem Register gespeichert sind, ausgeben zu können, ist es notwendig sich eine Textrepräsentierung der Zahl zu generieren. Die Zahl 123 wird also in den Text &amp;quot;123&amp;quot; umgewandelt welcher dann ausgegeben wird. Aus praktischen Gründen wird allerdings der Text nicht vollständig generiert (man müsste ihn ja irgendwo zwischenspeichern) sondern die einzelnen Buchstaben werden sofort ausgegeben, sobald sie bekannt sind.&lt;br /&gt;
&lt;br /&gt;
===Dezimal ausgeben===&lt;br /&gt;
Das Prinzip der Umwandlung ist einfach. Um herauszufinden wieviele Hunderter in der Zahl 123 enthalten sind, genügt es in einer Schleife immer wieder 100 von der Zahl abzuziehen und mitzuzählen wie oft dies gelang, bevor das Ergebnis negativ wurde. In diesem Fall lautet die Antwort: 1 mal, denn 123 - 100 macht 23. Versucht man erneut 100 anzuziehen, so ergibt sich eine negative Zahl.&lt;br /&gt;
Also muss eine &#039;1&#039; ausgeben werden. Die verbleibenden 23 werden weiterbehandelt, indem festgestellt wird wieviele Zehner darin enthalten sind. Auch hier wiederrum: In einer Schleife solange 10 abziehen, bis das Ergebnis nagativ wurde. Konkret geht das 2 mal gut, also muss das nächste auszugebende Zeichen ein &#039;2&#039; sein. Damit verbleiben noch die Einer, welche direkt in das entsprechende Zeichen umgewandelt werden können. In Summe hat man also an das Display die Zeichen &#039;1&#039; &#039;2&#039; &#039;3&#039; ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number:&lt;br /&gt;
           push  temp2            ; die Funktion verändert temp2, also sichern&lt;br /&gt;
                                  ; wir den Inhalt, um ihn am Ende wieder&lt;br /&gt;
                                  ; herstellen zu können&lt;br /&gt;
&lt;br /&gt;
           mov   temp2, temp1     ; das Register temp1 frei machen&lt;br /&gt;
                                  ; abzählen wieviele Hunderter&lt;br /&gt;
                                  ; in der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
           subi  temp2, 100       ; 100 abziehen&lt;br /&gt;
           brcs  lcd_number_2     ; ist dadurch ein Unterlauf entstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Hunderter mehr ...&lt;br /&gt;
           rjmp  lcd_number_1     ; ... und ab zur nächsten Runde&lt;br /&gt;
;&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorherhgehende Schleife 100 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; abzählen wieviele Zehner in&lt;br /&gt;
                  ; der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_3:&lt;br /&gt;
           subi  temp2, 10        ; 10 abziehen&lt;br /&gt;
           brcs  lcd_number_4     ; ist dadurch ein Unterlauf enstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Zehner mehr ...&lt;br /&gt;
           rjmp  lcd_number_3     ; ... und ab zur nächsten Runde&lt;br /&gt;
&lt;br /&gt;
                              ; die Zehnerstelle ausgeben&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; die übrig gebliebenen Einer&lt;br /&gt;
                  ; noch ausgeben&lt;br /&gt;
           ldi   temp1, &#039;0&#039;       ; die Zahl in temp2 ist jetzt im Bereich&lt;br /&gt;
           add   temp1, temp2     ; 0 bis 9. Einfach nur den ASCII Code für&lt;br /&gt;
           rcall lcd_data         ; &#039;0&#039; dazu addieren und wir erhalten dierekt&lt;br /&gt;
                                  ; den ASCII Code für die Ziffer&lt;br /&gt;
&lt;br /&gt;
           pop   temp2            ; den gesicherten Inhalt von temp2 wieder herstellen&lt;br /&gt;
           ret                    ; und zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beachte: Diese Funktion benutzt wiederrum die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039;. Anders als bei den bisherigen Aufrufen ist &#039;&#039;&#039;lcd_number&#039;&#039;&#039; aber darauf angewiesen, dass &#039;&#039;&#039;lcd_data&#039;&#039;&#039; das Register &#039;&#039;&#039;temp2&#039;&#039;&#039; unangetastet lässt. Falls sie es noch nicht getan haben, dann ist das jetzt die perfekte Gelegenheit, &#039;&#039;&#039;lcd_data&#039;&#039;&#039; mit den entsprechenden &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; Befehlen zu versehen. Sie sollten dies unbedingt zur Übung selbst machen. Am Ende muß die Funktion dann wie diese hier aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1           ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap  temp1                  ; Vertauschen&lt;br /&gt;
           andi  temp1, 0b00001111      ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr   temp1, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp1        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi  temp2, 0b00001111      ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr   temp2, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp2        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur ohne RS zu setzen&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           andi  temp1, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi  temp2, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz zur Funktionsweise der Funktion &#039;&#039;&#039;lcd_number&#039;&#039;&#039;: Die Zahl in einem Register bewegt sich im Wertebereich 0 bis 255. Um herauszufinden, wie die Hunderterstelle lautet, zieht die Funktion einfach in einer Schleife immer wieder 100 von der Schleife ab, bis bei der Subtraktion ein Unterlauf, angezeigt durch das Setzen des Carry-Bits bei der Subtraktion, entsteht. Die Anzahl wird im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; mitgezählt. Da dieses Register mit dem ASCII Code von &#039;0&#039; initialisiert wurde, und dieser ASCII Code bei jedem Schleifendurchlauf um 1 erhöht wird, können wir das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; direkt zur Ausgabe des Zeichens für die Hunderterstelle durch die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; benutzen. Völlig analog funktioniert auch die Ausgabe der Zehnerstelle.&lt;br /&gt;
&lt;br /&gt;
===Unterdrückung von führenden Nullen===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung: Diese Routine ist fehlerhaft&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt jede Zahl im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; immer mit 3 Stellen aus. Führende Nullen werden nicht unterdrückt. Möchte man dies ändern, so ist das ganz leicht möglich: Vor Ausgabe der Hunderterstelle bzw. Zehnerstelle muss lediglich überprüft werden, ob die Entsprechende Ausgabe eine &#039;0&#039; wäre. Ist sie das, so wird die Ausgabe übersprungen. Lediglich in der Einerstelle wird jede Ziffer wie errechnet ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ...&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben, wenn&lt;br /&gt;
                                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_2a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_2a:&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&lt;br /&gt;
           ...&lt;br /&gt;
                              ; die Zehnerstelle ausgeben, wenn&lt;br /&gt;
                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_4a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_4a:&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Verfahren, die einzelnen Stellen durch Subtraktion zu bestimmen, ist bei kleinen Zahlen eine durchaus gängige Alternative. Vor allem dann, wenn keine hardwaremäßige Unterstützung für Multiplikation und Division zur Verfügung steht. Ansonsten könnte man die die einzelnen Ziffern auch durch Division bestimmen. Das Prinzip ist folgendes (beispielhaft an der Zahl 52783 gezeigt)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   52783 / 10          -&amp;gt; 5278&lt;br /&gt;
   52783 - 5278 * 10   -&amp;gt;          3&lt;br /&gt;
&lt;br /&gt;
   5278 / 10           -&amp;gt; 527&lt;br /&gt;
   5278 - 527 * 10     -&amp;gt;          8&lt;br /&gt;
&lt;br /&gt;
   527 / 10            -&amp;gt; 52&lt;br /&gt;
   527 - 52 * 10       -&amp;gt;          7&lt;br /&gt;
&lt;br /&gt;
   52 / 10             -&amp;gt; 5&lt;br /&gt;
   52 - 5 * 10         -&amp;gt;          2&lt;br /&gt;
&lt;br /&gt;
   5 / 10              -&amp;gt; 0&lt;br /&gt;
   5 - 0 * 10          -&amp;gt;          5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Prinzip ist also die Restbildung bei einer fortgesetzten Division durch 10, wobei die einzelnen Ziffern in umgekehrter Reihenfolge ihrer Wertigkeit entstehen. Dadurch hat man aber ein Problem: Damit die Zeichen in der richtigen Reihenfolge ausgegeben werden können, muß man sie meistens zwischenspeichern um sie in der richtigen Reihenfole ausgeben zu können. Wird die Zahl in einem Feld von immer gleicher Größe ausgegeben, dann kann man auch die Zahl von rechts nach links ausgeben (bei einem LCD ist das möglich).&lt;br /&gt;
&lt;br /&gt;
===Hexadezimal ausgeben===&lt;br /&gt;
&lt;br /&gt;
Zu guter letzt hier noch eine Funktion, die eine Zahl aus dem Register &#039;&#039;&#039;temp1&#039;&#039;&#039; in hexadezimaler Form ausgibt. Die Funktion weist keine Besonderheiten auf und sollte unmittelbar verständlich sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen hexadezimal ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number_hex:&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           rcall lcd_number_hex_digit&lt;br /&gt;
           swap  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_number_hex_digit:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
           andi  temp1, $0F&lt;br /&gt;
           cpi   temp1, 10&lt;br /&gt;
           brlt  lcd_number_hex_digit_1&lt;br /&gt;
           subi  temp1, -( &#039;A&#039; - &#039;9&#039; - 1 ) ; es wird subi mit negativer Konstante verwendet, weil es kein addi gibt&lt;br /&gt;
lcd_number_hex_digit_1:&lt;br /&gt;
           subi  temp1, -&#039;0&#039;               ; ditto&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           &lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Eine 16-Bit Zahl aus einem Registerpärchen ausgeben===&lt;br /&gt;
&lt;br /&gt;
Um eine 16 Bit Zahl auszugeben wird wieder das bewährte Schema benutzt die einzelnen Stellen durch Subtraktion abzuzählen. Da es sich hierbei allerdings um eine 16 Bit Zahl handelt, müssen die Subtraktionen als 16-Bit Arithmetik ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 16 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp2 (low Byte) / temp3 (high Byte)&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number16:&lt;br /&gt;
           push  temp1&lt;br /&gt;
           push  temp2&lt;br /&gt;
           push  temp3&lt;br /&gt;
&lt;br /&gt;
; die Zehntausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number0:&lt;br /&gt;
           subi  temp2, low(10000)&lt;br /&gt;
           sbci  temp3, high(10000)&lt;br /&gt;
           brcs  lcd_number1&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number0&lt;br /&gt;
&lt;br /&gt;
; .. und ausgeben&lt;br /&gt;
lcd_number1:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-10000)&lt;br /&gt;
           sbci  temp3, high(-10000)&lt;br /&gt;
&lt;br /&gt;
; die Tausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number2:&lt;br /&gt;
           subi  temp2, low(1000)&lt;br /&gt;
           sbci  temp3, high(1000)&lt;br /&gt;
           brcs  lcd_number3&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number2&lt;br /&gt;
&lt;br /&gt;
; ... und ausgeben&lt;br /&gt;
lcd_number3:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-1000)&lt;br /&gt;
           sbci  temp3, high(-1000)&lt;br /&gt;
&lt;br /&gt;
; Als nächtes kommt die Hunderterstelle drann&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number4:&lt;br /&gt;
           subi  temp2, low(100)&lt;br /&gt;
           sbci  temp3, high(100)&lt;br /&gt;
           brcs  lcd_number5&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number4&lt;br /&gt;
&lt;br /&gt;
; und ausgeben&lt;br /&gt;
lcd_number5:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100&lt;br /&gt;
&lt;br /&gt;
; bleiben noch die Zehner&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number6:&lt;br /&gt;
           subi  temp2, 10&lt;br /&gt;
           brcs  lcd_number7&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number6&lt;br /&gt;
&lt;br /&gt;
; ausgeben ...&lt;br /&gt;
lcd_number7:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10&lt;br /&gt;
&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
           add   temp1, temp2&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
; fertig. Stack wieder aufräumen&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           pop   temp3&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Der überarbeitete, komplette Code==&lt;br /&gt;
&lt;br /&gt;
Hier also die komplett überarbeitete Version der LCD Funktionen.&lt;br /&gt;
&lt;br /&gt;
Die für die Benutzung relevanten Funktionen&lt;br /&gt;
* &#039;&#039;&#039;lcd_init&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_clear&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_home&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_data&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_flash_string&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number_hex&#039;&#039;&#039;&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; übergeben, wobei &#039;&#039;&#039;temp1&#039;&#039;&#039; vom Usercode definiert werden muss.&lt;br /&gt;
&lt;br /&gt;
[[Media:lcd-routines.asm|Download lcd-routines.asm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Stack|&lt;br /&gt;
zurücklink=AVR-Tutorial: Stack|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Interrupts|&lt;br /&gt;
vorlink=AVR-Tutorial: Interrupts}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister SREG) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register temp1 übergeben, wobei temp1 vom Usercode definiert werden muss. &lt;br /&gt;
&lt;br /&gt;
der progger hat vergessen das Z reg in den sub&#039;s lcd_flash_string ...&lt;br /&gt;
zu sichern, aber danke für den Einstig&lt;br /&gt;
&lt;br /&gt;
Lars&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=28000</id>
		<title>AVR-Tutorial: LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=28000"/>
		<updated>2008-05-16T03:33:27Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Einschub: Code aufräumen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kaum ein elektronisches Gerät kommt heutzutage noch ohne ein LCD daher. Ist doch auch praktisch, Informationen im Klartext anzeigen zu können, ohne irgendwelche LEDs blinken zu lassen. Kein Wunder also, dass die häufigste Frage in Mikrocontroller-Foren ist: &amp;quot;Wie kann ich ein LCD anschließen?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-Bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-Bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-Bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-Bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-Bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-Bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4 Bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert. Ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitet hat. Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt, da wir wissen, welche Anschlüsse das LCD benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Pinnummer&amp;lt;BR&amp;gt;LCD || Bezeichnung || Anschluss&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |1 || Vss || GND&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |2 || Vcc || 5V&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |3 || Vee || GND oder [[Potentiometer | Poti]] &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |4 || RS || PD4 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |5 || RW || GND &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |6 || E || PD5 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |7 || DB0 || nicht angeschlossen &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |8 || DB1 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |9 || DB2 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |10 || DB3 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |11 || DB4 || PD0 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |12 || DB5 || PD1 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |13 || DB6 || PD2 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |14 || DB7 || PD3 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |15 || A || Vorsicht! Meistens nicht direkt an +5V anschließbar,&amp;lt;BR&amp;gt;nur über einen Vorwiderstand (z.B. 33&amp;amp;Omega;)!&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |16 || K || GND&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Ok alles ist verbunden. Wenn man jetzt den Strom einschaltet, sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display?&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung des LCDs im 4-Bit-Modus ==&lt;br /&gt;
&lt;br /&gt;
Um ein Byte zu übertragen, muss man es erstmal in die beiden Nibbles zerlegen, die getrennt übertragen werden. Da das obere Nibble (Bit 4 - Bit 7) als erstes übertragen wird, die 4 Datenleitungen jedoch an die vier unteren Bits des Port D angeschlossen sind, muss man die beiden Nibbles des zu übertragenden Bytes erstmal vertauschen. Der AVR kennt dazu praktischerweise einen eigenen Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           swap r16               ; vertauscht die beiden Nibbles von r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus 0b00100101 wird so z.B. 0b01010010. &lt;br /&gt;
&lt;br /&gt;
Jetzt sind die Bits für die erste Phase der Übertragung an der richtigen Stelle. Trotzdem wollen wir das Ergebnis nicht einfach so mit &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; an den Port geben. Um die Hälfte des Bytes, die jetzt nicht an die Datenleitungen des LCDs gegeben wird auf null zu setzen, verwendet man folgenden Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           andi r16, 0b00001111   ; Nur die vier unteren (mit 1 markierten)&lt;br /&gt;
                                  ; Bits werden übernommen, alle anderen werden null&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also: Das obere Nibble wird erst mit dem unteren vertauscht, damit es unten ist. Dann wird das obere (das wir jetzt noch nicht brauchen) auf null gesetzt. &lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem LCD noch mitteilen, ob wir Daten oder Befehle senden wollen. Das machen wir, indem wir das Bit, an dem RS angeschlossen ist (PD4), auf 0 (Befehl senden) oder auf 1 (Daten senden) setzen. Um ein Bit in einem normalen Register zu setzen, gibt es den Befehl sbr (Set Bit in Register). Dieser Befehl unterscheidet sich jedoch von sbi (das nur für IO-Register gilt) dadurch, dass man nicht die Nummer des zu setzenden Bits angibt, sondern eine Bitmaske. Das geht so: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbr r16, 0b00010000     ; Bit 4 setzen, alle anderen Bits bleiben gleich&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RS ist an PD4 angeschlossen. Wenn wir r16 an den Port D ausgeben, ist RS jetzt also high und das LCD erwartet Daten anstatt von Befehlen. &lt;br /&gt;
&lt;br /&gt;
Das Ergebnis können wir jetzt endlich direkt an den Port D übergeben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           out PORTD, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss vorher der Port D auf Ausgang geschalten werden, indem man 0xFF ins Datenrichtungsregister DDRD schreibt. &lt;br /&gt;
&lt;br /&gt;
Um dem LCD zu signalisieren, dass es das an den Datenleitungen anliegende Nibble übernehmen kann, wird die E-Leitung (Enable, an PD5 angeschlossen) auf high und kurz darauf wieder auf low gesetzt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbi PORTD, 5              ; Enable high&lt;br /&gt;
           nop                       ; 3 Taktzyklen warten (&amp;quot;nop&amp;quot; = nichts tun)&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5              ; Enable wieder low&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die eine Hälfte des Bytes wäre damit geschafft! Die andere Hälfte kommt direkt hinterher: Alles, was an der obenstehenden Vorgehensweise geändert werden muss, ist, das &amp;quot;swap&amp;quot; (Vertauschen der beiden Nibbles) wegzulassen.&lt;br /&gt;
&lt;br /&gt;
== Initialisierung des Displays ==&lt;br /&gt;
&lt;br /&gt;
Allerdings gibt es noch ein Problem. Wenn ein LCD eingeschaltet wird, dann läuft es zunächst im 8 Bit Modus. Irgendwie muss das Display initialisiert und auf den 4 Bit Modus umgeschaltet werden, und zwar nur mit den 4 zur Verfügung stehenden Datenleitungen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn es Probleme gibt, dann meistens an diesem Punkt. Die &amp;quot;kompatiblen&amp;quot; Kontroller sind gelegentlich doch nicht 100% identisch. Es lohnt sich, das Datenblatt (siehe Weblinks im Artikel [[LCD]]) genau zu lesen, in welcher Reihenfolge und mit welchen Abständen (Delays) die Initialisierungbefehle gesendet werden. Eine weitere Hilfe können Ansteuerungsbeispiele in Forenbeiträgen geben z.B.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79609#664268 (A) KS0066U oder Ähnliche --- LCD Treiber]&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 4 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Achtung: Im Folgenden sind alle Bytes aus Sicht des LCD-Kontrollers angegeben! Da LCD-seitig nur die Leitungen DB4 - DB7 verwendet werden, ist daher immer nur das höherwertige Nibble gültig. Durch die Art der Verschaltung (DB4 - DB7 wurde auf dem PORT an PD0 bis PD3 angeschlossen) ergibt sich eine Verschiebung, so dass das am Kontroller auszugebende Byte nibblemässig vertauscht ist!&lt;br /&gt;
&lt;br /&gt;
Die Sequenz, aus Sicht des Kontrollers, sieht so aus:&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* $2 ins Steuerregister schreiben (RS = 0), dadurch wird auf 4 Bit Daten umstellt&lt;br /&gt;
* Ab jetzt muss für die Übertragung eines Bytes jeweils zuerst das höherwertige Nibble und dann das niederwertige Nibble übertragen werden, wie oben beschrieben&lt;br /&gt;
* Mit dem Konfigurier-Befehl $20 das Display konfigurieren (4-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 8 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Der Vollständigkeit halber hier noch die notwendige Initialiserungssequenz für den 8 Bit Modus. Da hier die Daten komplett als 1 Byte übertragen werden können, sind einige Klimmzüge wie im 4 Bit Modus nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mit dem Konfigurier-Befehl 0x30 das Display konfigurieren (8-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
== Routinen zur LCD-Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
Die Routinen zur Kommunikation mit dem LCD sehen also so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000 (Anm.1)&lt;br /&gt;
           out PORTD, temp1             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000&lt;br /&gt;
           out PORTD, temp2             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out PORTD, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
 ;&lt;br /&gt;
 ; Bei höherem Takt (&amp;gt;= 8 MHz) kann es notwendig sein, &lt;br /&gt;
 ; vor dem Enable High 1-2 Wartetakte (nop) einzufügen. &lt;br /&gt;
 ; Siehe dazu http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi PORTD, 5                 ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5                 ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
&lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi  temp3,50&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall  delay5ms&lt;br /&gt;
           dec  temp3&lt;br /&gt;
           brne powerupwait&lt;br /&gt;
           ldi temp1, 0b00000011        ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out PORTD, temp1             ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00000010        ; 4bit-Modus einstellen&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00101000        ; 4Bit / 2 Zeilen / 5x8&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00001100        ; Display ein / Cursor aus / kein Blinken&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00000100        ; inkrement / kein Scrollen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi temp1, 0b00000001   ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi temp1, 0b00000010   ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anm.1: Siehe [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
Weitere Funktionen (wie z.B. Cursorposition verändern) sollten mit Hilfe der [[AVR-Tutorial:_LCD#Welche_Befehle_versteht_das_LCD.3F|Befehlscodeliste]] nicht schwer zu realisieren sein. Einfach den Code in temp laden, lcd_command aufrufen und ggf. eine Pause einfügen. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man die LCD-Ansteuerung auch an einen anderen Port des Mikrocontrollers &amp;quot;verschieben&amp;quot;: Wenn das LCD z.B. an Port B angeschlossen ist, dann reicht es, im Programm alle &amp;quot;PORTD&amp;quot; durch &amp;quot;PORTB&amp;quot; und &amp;quot;DDRD&amp;quot; durch &amp;quot;DDRB&amp;quot; zu ersetzen. &lt;br /&gt;
&lt;br /&gt;
Wer eine höhere Taktfrequenz als 4 MHz verwendet, der sollte daran denken, die Dauer der Verzögerungsschleifen anzupassen.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das diese Routinen zur Anzeige von Text verwendet, kann z.B. so aussehen (die Datei lcd-routines.asm muss sich im gleichen Verzeichnis befinden). Nach der Initialisierung wird zuerst der Displayinhalt gelöscht. Um dem LCD ein Zeichen zu schicken, lädt man es in temp1 und ruft die Routine &amp;quot;lcd_data&amp;quot; auf. Das folgende Beispiel zeigt das Wort &amp;quot;Test&amp;quot; auf dem LCD an. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/lcd-test.asm Download lcd-test.asm] &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;
&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, 0xFF    ; Port D = Ausgang&lt;br /&gt;
           out DDRD, temp1&lt;br /&gt;
&lt;br /&gt;
           rcall lcd_init     ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear    ; Display löschen&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;T&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;e&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           &lt;br /&gt;
           ldi temp1, &#039;s&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;t&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD-Routinen werden hier eingefügt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für längere Texte ist die Methode, jedes Zeichen einzeln in das Register zu laden und &amp;quot;lcd_data&amp;quot; aufzurufen natürlich nicht sehr praktisch. Dazu später aber mehr.&lt;br /&gt;
&lt;br /&gt;
Bisher wurden in Register immer irgendwelche Zahlenwerte geladen, aber in diesem Programm kommt plötzlich die Anweisung&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
vor. Wie ist diese zu verstehen? Passiert hier etwas grundlegend anderes als beim Laden einer Zahl in ein Register?&lt;br /&gt;
&lt;br /&gt;
Die Antwort darauf lautet: Nein. Auch hier wird letztendlich nur eine Zahl in ein Register geladen. Der Schlüssel zum Verständnis beruht darauf, dass zum LCD, so wie zu allen Ausgabegeräten, für die Ausgabe von Texten immer nur Zahlen übertragen werden, sog. Codes. Zum Beispiel könnte man vereinbaren, dass ein LCD, wenn es den Ausgabecode 65 erhält, ein &#039;A&#039; anzeigt, bei einem Ausgabecode von 66 ein &#039;B&#039; usw. Naturgemäß gibt es daher viele verschiedene Code-Buchstaben Zuordnungen. Damit hier etwas Ordnung in das potentielle Chaos kommt, hat man sich bereits in der Steinzeit der Programmierung auf bestimmte Codetabellen geeinigt, von denen die verbreitetste sicherlich die ASCII-Zuordnung ist.&lt;br /&gt;
&lt;br /&gt;
==ASCII==&lt;br /&gt;
&lt;br /&gt;
ASCII steht für &#039;&#039;American Standard Code for Information Interchange&#039;&#039; und ist ein standardisierter Code zur Zeichenumsetzung. Die Codetabelle sieht hexadezimal dabei wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;.0&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x1&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x2&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x3&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x4&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x6&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x7&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x8&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x9&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xA&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xB&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xC&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xD&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xE&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xF&amp;lt;/th&amp;gt;&amp;lt;br&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;&#039;&#039;&#039;0x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NUL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SOH&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;STX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EOT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ENQ&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ACK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BEL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;HT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;VT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CR&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SO&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SI&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;&#039;&#039;&#039;1x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DLE&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NAK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SYN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CAN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EM&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SUB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ESC&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;US&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;&#039;&#039;&#039;2x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SP&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;!&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;#&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;%&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;amp;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;(&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;)&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;*&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;+&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;,&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;.&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;3x&#039;&#039;&#039;&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;&amp;lt;td&amp;gt;3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;9&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;=&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;gt;&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;4x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;B&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;C&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;D&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;F&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;G&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;H&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;I&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;J&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;L&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;M&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;N&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;O&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;&#039;&#039;&#039;5x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;P&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;R&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;S&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;T&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;U&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;V&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;W&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;X&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;[&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;\&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;]&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;^&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;6x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;`&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;a&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;b&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;c&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;d&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;e&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;f&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;g&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;h&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;i&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;j&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;k&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;l&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;m&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;n&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;o&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;&#039;&#039;&#039;7x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;p&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;r&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;s&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;t&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;u&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;v&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;w&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;{&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;|&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;~&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DEL&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 ersten beiden Zeilen enthalten die Codes für einige Steuerzeichen, ihre vollständige Beschreibung würde hier zu weit führen. Das Zeichen &#039;&#039;&#039;SP&#039;&#039;&#039; steht für ein &#039;&#039;Space&#039;&#039;, also ein Leerzeichen. &#039;&#039;&#039;BS&#039;&#039;&#039; steht für &#039;&#039;Backspace&#039;&#039;, also ein Zeichen zurück. &#039;&#039;&#039;DEL&#039;&#039;&#039; steht für &#039;&#039;Delete&#039;&#039;, also das Löschen eines Zeichens. &#039;&#039;&#039;CR&#039;&#039;&#039; steht für &#039;&#039;Carriage Return&#039;&#039;, also wörtlich: der Wagenrücklauf (einer Schreibmaschine), während &#039;&#039;&#039;LF&#039;&#039;&#039; für &#039;&#039;Line feed&#039;&#039;, also einen Zeilenvorschub steht.&lt;br /&gt;
&lt;br /&gt;
Der Assembler kennt diese Codetabelle und ersetzt die Zeile&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, $54&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
was letztendlich auch der Lesbarkeit des Programmes zugute kommt. Funktional besteht kein Unterschied zwischen den beiden Anweisungen. Beide bewirken, dass das Register temp1 mit dem Bitmuster 01010100 ( = hexadezimal 54, = dezimal 84 oder eben der ASCII Code für &#039;&#039;&#039;T&#039;&#039;&#039;) geladen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das LCD wiederrum kennt diese Code-Tabelle ebenfalls und wenn es über den Datenbus die Codezahl $54 zur Anzeige empfängt, dann schreibt es ein &#039;&#039;&#039;T&#039;&#039;&#039; an die aktuelle Cursorposition. Genauer gesagt, weiss das LCD nichts von einem &#039;&#039;&#039;T&#039;&#039;&#039;. Es sieht einfach in seinen internen Tabellen nach, welche Pixel beim Empfang der Codezahl $54 auf schwarz zu setzen sind. &#039;Zufällig&#039; sind das genau jene Pixel, die für uns Menschen ein &#039;&#039;&#039;T&#039;&#039;&#039; ergeben.&lt;br /&gt;
&lt;br /&gt;
==Welche Befehle versteht das LCD?==&lt;br /&gt;
&lt;br /&gt;
Auf dem LCD arbeitet ein Kontroller vom Typ HD44780. Dieser Kontroller versteht eine Reihe von Befehlen, die allesamt mittels lcd_command gesendet werden können. Ein Kommando ist dabei nichts anderes als ein Befehlsbyte, in dem die verschiedenen Bits verschiedene Bedeutungen haben:&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 0 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 1 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;der Zustand dieses Bits ist egal&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;sonstige Buchstaben&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;das Bit muss je nach gewünschter Funktionalität gesetzt werden. Die mögliche Funktionalität des jeweiligen Bits geht aus der Befehlsbeschreibung hervor&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Kommando &#039;ON/OFF Control&#039; soll benutzt werden, um das Display einzuschalten, der Cursor soll eingeschaltet werden und der Cursor soll blinken.&lt;br /&gt;
Das Befehlsbyte ist so aufgebaut:&lt;br /&gt;
   0b00001dcb&lt;br /&gt;
Aus der Befehlsbeschreibung entnimmt man:&lt;br /&gt;
* Display ein bedeutet, dass an der Bitposition d eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor ein bedeutet, dass an der Bitposition c ein 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor blinken bedeutet, dass an der Bitposition b eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
Das dafür zu übertragende Befehlsbyte hat also die Gestalt 0b00001111 oder in hexadezimaler Schreibweise $0F.&lt;br /&gt;
&lt;br /&gt;
===Clear display: 0b00000001===&lt;br /&gt;
&lt;br /&gt;
Die Anzeige wird gelöscht und der Ausgabecursor kehrt an die Home Position (links, erste Zeile) zurück.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Cursor home: 0b0000001x===&lt;br /&gt;
&lt;br /&gt;
Der Cursor kehrt an die Home Position (links, erste Zeile) zurück. Ein verschobenes Display wird auf die Grundeinstellung zurückgesetzt.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs bis 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Entry mode: 0b000001is===&lt;br /&gt;
&lt;br /&gt;
Legt die Cursor Richtung sowie eine mögliche Verschiebung des Displays fest&lt;br /&gt;
* i = 1, Cursorposition bei Ausgabe eines Zeichens erhöhen&lt;br /&gt;
* i = 0, Cursorposition bei Ausgabe eines Zeichens vermindern&lt;br /&gt;
* s = 1, Display wird gescrollt, wenn der Cursor das Ende/Anfang, je nach Einstellung von i, erreicht hat.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===On/off control: 0b00001dcb===&lt;br /&gt;
&lt;br /&gt;
Display insgesamt ein/ausschalten; den Cursor ein/ausschalten; den Cursor auf blinken schalten/blinken aus. Wenn das Display ausgeschaltet wird, geht der Inhalt des Displays nicht verloren. Der vorher angezeigte Text wird nach wiedereinschalten erneut angezeigt.&lt;br /&gt;
Ist der Cursor eingeschaltet, aber Blinken ausgeschaltet, so wird der Cursor als Cursorzeile in Pixelzeile 8 dargestellt. Ist Blinken eingeschaltet, wird der Cursor als blinkendes ausgefülltes Rechteck dargestellt, welches abwechselnd mit dem Buchstaben an dieser Stelle angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
* d = 0, Display aus&lt;br /&gt;
* d = 1, Display ein&lt;br /&gt;
* c = 0, Cursor aus&lt;br /&gt;
* c = 1, Cursor ein&lt;br /&gt;
* b = 0, Cursor blinken aus&lt;br /&gt;
* b = 1, Cursor blinken ein&lt;br /&gt;
 &lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Cursor/Scrollen: 0b0001srxx===&lt;br /&gt;
&lt;br /&gt;
Bewegt den Cursor oder scrollt das Display um eine Position entweder nach rechts oder nach links.&lt;br /&gt;
&lt;br /&gt;
* s = 1, Display scrollen&lt;br /&gt;
* s = 0, Cursor bewegen&lt;br /&gt;
* r = 1, nach rechts&lt;br /&gt;
* r = 0, nach links &lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Konfiguration: 0b001dnfxx===&lt;br /&gt;
&lt;br /&gt;
Einstellen der Interface Art, Modus, Font&lt;br /&gt;
* d = 0, 4-Bit Interface&lt;br /&gt;
* d = 1, 8-Bit Interface&lt;br /&gt;
* n = 0, 1 zeilig&lt;br /&gt;
* n = 1, 2 zeilig&lt;br /&gt;
* f = 0, 5x7 Pixel&lt;br /&gt;
* f = 1, 5x11 Pixel&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Character RAM Address Set: 0b01aaaaaa===&lt;br /&gt;
&lt;br /&gt;
Mit diesem Kommando werden maximal 8 selbst definierte Zeichen definiert. Dazu wird der Character RAM Zeiger auf den Anfang des Character Generator (CG) RAM gesetzt und das Zeichen durch die Ausgabe von 8 Byte definiert. Der Adresszeiger wird nach Ausgabe jeder Pixelzeile (8 Bit) vom LCD selbst erhöht. Nach Beendigung der Zeichendefinition muss die Schreibposition explizit mit dem Kommando &amp;quot;Display RAM Address Set&amp;quot; wieder in den DD-RAM Bereich gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
aaaaaa 6-bit CG RAM Adresse&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Display RAM Address Set: 0b1aaaaaaa===&lt;br /&gt;
&lt;br /&gt;
Den Cursor neu positionieren. Display Data (DD) Ram ist vom Character Generator (CG) Ram unabhängig. Der Adresszeiger wird bei Ausgabe eines Zeichens ins DD Ram automatisch erhöht. Das Display verhält sich so, als ob eine Zeile immer aus 32 logischen Zeichen besteht, von der, je nach konkretem Displaytyp (16 Zeichen, 20 Zeichen) immer nur ein Teil sichtbar ist.&lt;br /&gt;
&lt;br /&gt;
aaaaaaa 7-bit DD RAM Adresse. Auf 2-zeiligen Displays (und den meisten 16x1 Displays), kann die Adressangabe wie folgt interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
1laaaaaa&lt;br /&gt;
* l = Zeilennummer (0 oder 1)&lt;br /&gt;
* a = 6-Bit Spaltennummer&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
==Einschub: Code aufräumen==&lt;br /&gt;
&lt;br /&gt;
Es wird Zeit, sich einmal etwas kritisch mit den bisher geschriebenen Funktionen auseinander zu setzen.&lt;br /&gt;
&lt;br /&gt;
===Portnamen aus dem Code herausziehen===&lt;br /&gt;
&lt;br /&gt;
Wenn wir die LCD-Funktionen einmal genauer betrachten, dann fällt sofort auf, dass über die Funktionen verstreut immer wieder das &#039;&#039;&#039;PORTD&#039;&#039;&#039; sowie einzelne Zahlen für die Pins an diesem Port auftauchen. Wenn das LCD an einem anderen Port betrieben werden soll, oder sich die Pin-Belegung ändert, dann muss an all diesen Stellen eine Anpassung vorgenommen werden. Dabei darf keine einzige Stelle übersehen werden, ansonsten würden die LCD-Funktionen nicht oder nicht vollständig funktionieren.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit, dem vorzubeugen, ist es, diese immer gleichbleibenden Dinge an den Anfang der LCD-Funktionen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
.equ PIN_E    = 5&lt;br /&gt;
.equ PIN_RS   = 4&lt;br /&gt;
&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp1          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp2          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi LCD_PORT, PIN_E          ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi LCD_PORT, PIN_E          ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
 &lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf Ausgang&lt;br /&gt;
           out   LCD_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi   temp3,6&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           dec   temp3&lt;br /&gt;
           brne  powerupwait&lt;br /&gt;
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out   LCD_PORT, temp1        ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00101000      ; 4 Bot, 2 Zeilen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00001100      ; Display on, Cursor off&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00000100      ; endlich fertig&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi   temp1, 0b00000010      ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels &#039;&#039;&#039;.equ&#039;&#039;&#039; werden mit dem Assembler Textersetzungen vereinbart. Der Assembler ersetzt alle Vorkomnisse des Quelltextes durch den zu ersetzenden Text. Dadurch ist es z.B. möglich, alle Vorkommnisse von &#039;&#039;&#039;PORTD&#039;&#039;&#039; durch &#039;&#039;&#039;LCD_PORT&#039;&#039;&#039; auszutauschen. Wird das LCD an einen anderen Port, z.B. &#039;&#039;&#039;PORTB&#039;&#039;&#039; gelegt, dann genügt es, die Zeilen&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTB&lt;br /&gt;
.equ LCD_DDR  = DDRB&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu ersetzen. Der Assembler sorgt dann dafür, dass diese Portänderung an den relevanten Stellen im Code über die Textersetzungen einfließt. Selbiges natürlich mit der Pin-Zuordnung.&lt;br /&gt;
&lt;br /&gt;
===Registerbenutzung===&lt;br /&gt;
&lt;br /&gt;
Bei diesen Funktionen mussten einige Register des Prozessors benutzt werden, um darin Zwischenergebnisse zu speichern bzw. zu bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss dabei natürlich, dass es zu keinen Überschneidungen kommt. Solange nur jede Funktion jeweils für sich betrachtet wird, ist das kein Problem. In 20 oder 30 Code-Zeilen kann man gut verfolgen, welches Register wofür benutzt wird. Schwieriger wird es, wenn Funktionen wiederum andere Funktionen aufrufen, die ihrerseits wieder Funktionen aufrufen usw. Jede dieser Funktionen benutzt einige Register und mit zunehmender Programmgröße wird es immer schwieriger, zu verfolgen, welches Register zu welchem Zeitpunkt wofür benutzt wird.&lt;br /&gt;
&lt;br /&gt;
Speziell bei Basisfunktionen wie diesen LCD-Funktionen, ist es daher oft ratsam, dafür zu sorgen, dass jede Funktion die Register wieder in dem Zustand hinterlässt, indem sie sie auch vorgefunden hat. Wir benötigen dazu wieder den Stack, auf dem die Registerinhalte bei Betreten einer Funktion zwischengespeichert werden und von dem die Register bei Verlassen einer Funktion wiederhergestellt werden.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir die Funktion&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion verändert das Register temp1. Um das Register abzusichern, schreiben wir die Funktion um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           push  temp1                  ; temp1 auf dem Stack sichern&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           pop   temp1                  ; temp1 vom Stack wiederherstellen&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am besten hält man sich an die Regel: Jede Funktion ist dafür zuständig, die Register zu sichern und wieder herzustellen, die sie auch selbst verändert. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; ruft die Funktionen &#039;&#039;&#039;lcd_command&#039;&#039;&#039; und &#039;&#039;&#039;delay5ms&#039;&#039;&#039; auf. Wenn diese Funktionen selbst wieder Register verändern (und das tun sie), so ist es die Aufgabe dieser Funktionen, sich um die Sicherung und das Wiederherstellen der entsprechenden Register zu kümmern. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; sollte sich nicht darum kümmern müssen. Auf diese Weise ist das Schlimmste, das einem passieren kann, dass ein paar Register unnütz gesichert und wiederhergestellt werden. Das kostet zwar etwas Rechenzeit und etwas Speicherplatz auf dem Stack, ist aber immer noch besser als das andere Extrem: Nach einem Funktionsaufruf haben einige Register nicht mehr den Wert, den sie haben sollten, und das Programm rechnet mit falschen Zahlen weiter.&lt;br /&gt;
&lt;br /&gt;
===Lass den Assembler rechnen===&lt;br /&gt;
Betrachtet man den Code genauer, so fallen einige konstante Zahlenwerte auf (Das vorangestellte $ kennzeichnet die Zahl als Hexadezimalzahl):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Code benötigt eine Warteschleife, die mindestens 50µs dauert. Die beiden Befehle innerhalb der Schleife benötigen 3 Takte: 1 Takt für den &#039;&#039;&#039;dec&#039;&#039;&#039; und der &#039;&#039;&#039;brne&#039;&#039;&#039; benötigt 2 Takte, wenn die Bedingung zutrifft, der Branch also genommen wird. Bei 4 Mhz werden also 4000000 / 3 * 50 / 1000000 = 66.6 Durchläufe durch die Schleife benötigt, um eine Verzögerungszeit von 50µs (0.000050 Sekunden) zu erreichen, hexadezimal ausgedrückt: $42.&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist: Bei anderen Taktfrequenzen müsste man nun jedesmal diese Berechnung machen und den entsprechenden Zahlenwert einsetzen. Das kann aber der Assembler genausogut erledigen. Am Anfang des Codes wird ein Eintrag definiert, der die Taktfrequenz festlegt. Traditionell heißt dieser Eintrag &amp;lt;i&amp;gt;XTAL&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ XTAL  = 4000000&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 50 / 3 ) / 1000000&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An einer anderen Codestelle gibt es weitere derartige magische Zahlen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was geht hier vor?&lt;br /&gt;
Die innere Schleife benötigt wieder 3 Takte pro Durchlauf. Bei $C9 = 201 Durchläufen werden also 201 * 3 = 603 Takte verbraucht. In der äußeren Schleife werden pro Durchlauf alo 1 + 603 + 1 + 2 = 607 Takte verbraucht. Da die äußere Schleife $21 = 33 mal wiederholt wird, werden 20031 Takte verbraucht. Bei 4Mhz benötigt der Prozessor 20031 / 4000000 = 0.005007 Sekunden, also 5 ms.&lt;br /&gt;
Wird der Wiederholwert für die innere Schleife bei $C9 belassen, so werden 4000000 / 607 * 5 / 1000 Wiederholungen der äusseren Schleife benötigt. Auch diese Berechnung kann wieder der Assembler übernehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 5 / 607 ) / 1000&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein kleines Problem kann bei der Verwendung dieses Verfahrens entstehen: Bei hohen Taktfrequenzen und großen Wartezeiten kann der berechnete Wert größer als 255 werden und man bekommt die Fehlermeldung &amp;quot;Operand(s) out of range&amp;quot; beim Assemblieren. Dieser Fall tritt zum Beispiel für obige Konstruktion bei einer Taktfrequenz von 16 MHz ein (genauer gesagt ab 15,3 MHz), während darunter XTAL beliebig geändert werden kann. Als einfachste Lösung bietet es sich an, die Zahl der Takte pro Schleifendurchlauf durch das Einfügen von &#039;&#039;&#039;nop&#039;&#039;&#039; zu erhöhen und die Berechnungsvorschrift anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Ausgabe eines konstanten Textes ==&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde schon einmal ein Text ausgegeben. Dies geschah durch Ausgabe von einzelnen Zeichen. Das können wir auch anders machen. Wir können den Text im Speicher ablegen und eine Funktion schreiben, die die einzelnen Zeichen aus dem Speicher holt und ausgibt. Dabei erhebt sich aber eine Fragestellung: Woher weiß die Funktion eigentlich, wie lange der Text ist? Die Antwort darauf lautet: Sie kann es nicht wissen. Wir müssen irgendwelche Vereinbarungen treffen, woran die Funktion erkennen kann, dass der Text zu Ende ist. Im Wesentlichen werden dazu 2 Methoden benutzt:&lt;br /&gt;
* Der Text enthält ein spezielles Zeichen, welches das Ende des Textes markiert&lt;br /&gt;
* Wir speichern nicht nur den Text selbst, sondern auch die Länge des Textes&lt;br /&gt;
Mit einer der beiden Methoden ist es der Textausgabefunktion dann ein Leichtes, den Text vollständig auszugeben.&lt;br /&gt;
&lt;br /&gt;
Wir werden uns im Weiteren dafür entscheiden, ein spezielles Zeichen, eine 0, dafür zu benutzen. Die Ausgabefunktionen werden dann etwas einfacher, als wenn bei der Ausgabe die Anzahl der bereits ausgegebenen Zeichen mitgezählt werden muss.&lt;br /&gt;
&lt;br /&gt;
Den Text selbst speichern wir im Flash-Speicher, also dort wo auch das Programm gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Einen konstanten Text aus dem Flash Speicher&lt;br /&gt;
 ; ausgeben. Der Text wird mit einer 0 beendet&lt;br /&gt;
lcd_flash_string:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_1:&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           rjmp  lcd_flash_string_1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_2:&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion benutzt den Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039; um das jeweils nächste Zeichen aus dem Flash Speicher in ein Register zur Weiterverarbeitung zu laden. Dazu wird der sog. &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; benutzt. So nennt man das Registerpaar &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;. Nach jedem Ladevorgang wird dabei durch den Befehl&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
dieser Z-Pointer um 1 erhöht. Mittels &#039;&#039;&#039;cpi&#039;&#039;&#039; wird das in das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; geladene Zeichen mit 0 verglichen. &#039;&#039;&#039;cpi&#039;&#039;&#039; vergleicht die beiden Zahlen und merkt sich das Ergebnis in einem speziellen Register in Form von Status Bits. &#039;&#039;&#039;cpi&#039;&#039;&#039; zieht dabei ganz einfach die beiden Zahlen voneinander ab. Sind sie gleich, so kommt da als Ergebnis 0 heraus und &#039;&#039;&#039;cpi&#039;&#039;&#039; setzt daher konsequenter Weise das Zero-Flag, das anzeigt, daß die vorhergegangene Operation ein 0 Ergebnis hatte.&#039;&#039;&#039;breq&#039;&#039;&#039; wertet diese Status-Bits aus. Wenn die vorhergegangene Operation ein 0-Ergebnis hatte, das Zero-Flag also gesetzt ist, dann wird ein Sprung zum angegebenen Label durchgeführt. In Summe bewirkt also die Sequenz&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
das das gelesene Zeichen mit 0 verglichen wird und falls das gelesene&lt;br /&gt;
Zeichen tatsächlich 0 war, an der Stelle lcd_flash_string_2 weiter gemacht wird. Im anderen Fall wird die bereits geschriebene Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aufgerufen, welche das Zeichen ausgibt. &#039;&#039;&#039;lcd_data&#039;&#039;&#039; erwartet dabei das Zeichen im Register &#039;&#039;&#039;temp1&#039;&#039;&#039;, genau in dem Register in welches wir vorher mittels &#039;&#039;&#039;lpm&#039;&#039;&#039; das Zeichen geladen hatten.&lt;br /&gt;
&lt;br /&gt;
Das verwendende Programm sieht dann 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 temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
           rcall lcd_init              ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear             ; Display löschen&lt;br /&gt;
 &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;
&lt;br /&gt;
           rcall lcd_flash_string      ; Unterprogramm gibt String aus der&lt;br /&gt;
                                       ; durch den Z-Pointer adressiert wird&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
           .db &amp;quot;Test&amp;quot;,0                ; Stringkonstante, durch eine 0&lt;br /&gt;
                                       ; abgeschlossen  &lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD Funktionen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Genaueres über die Verwendung unterschiedlicher Speicher findet sich im Kapitel [[AVR-Tutorial:_Speicher|Speicher]]&lt;br /&gt;
&lt;br /&gt;
==Zahlen ausgeben==&lt;br /&gt;
Um Zahlen, die beispielsweise in einem Register gespeichert sind, ausgeben zu können, ist es notwendig sich eine Textrepräsentierung der Zahl zu generieren. Die Zahl 123 wird also in den Text &amp;quot;123&amp;quot; umgewandelt welcher dann ausgegeben wird. Aus praktischen Gründen wird allerdings der Text nicht vollständig generiert (man müsste ihn ja irgendwo zwischenspeichern) sondern die einzelnen Buchstaben werden sofort ausgegeben, sobald sie bekannt sind.&lt;br /&gt;
&lt;br /&gt;
===Dezimal ausgeben===&lt;br /&gt;
Das Prinzip der Umwandlung ist einfach. Um herauszufinden wieviele Hunderter in der Zahl 123 enthalten sind, genügt es in einer Schleife immer wieder 100 von der Zahl abzuziehen und mitzuzählen wie oft dies gelang, bevor das Ergebnis negativ wurde. In diesem Fall lautet die Antwort: 1 mal, denn 123 - 100 macht 23. Versucht man erneut 100 anzuziehen, so ergibt sich eine negative Zahl.&lt;br /&gt;
Also muss eine &#039;1&#039; ausgeben werden. Die verbleibenden 23 werden weiterbehandelt, indem festgestellt wird wieviele Zehner darin enthalten sind. Auch hier wiederrum: In einer Schleife solange 10 abziehen, bis das Ergebnis nagativ wurde. Konkret geht das 2 mal gut, also muss das nächste auszugebende Zeichen ein &#039;2&#039; sein. Damit verbleiben noch die Einer, welche direkt in das entsprechende Zeichen umgewandelt werden können. In Summe hat man also an das Display die Zeichen &#039;1&#039; &#039;2&#039; &#039;3&#039; ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number:&lt;br /&gt;
           push  temp2            ; die Funktion verändert temp2, also sichern&lt;br /&gt;
                                  ; wir den Inhalt, um ihn am Ende wieder&lt;br /&gt;
                                  ; herstellen zu können&lt;br /&gt;
&lt;br /&gt;
           mov   temp2, temp1     ; das Register temp1 frei machen&lt;br /&gt;
                                  ; abzählen wieviele Hunderter&lt;br /&gt;
                                  ; in der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
           subi  temp2, 100       ; 100 abziehen&lt;br /&gt;
           brcs  lcd_number_2     ; ist dadurch ein Unterlauf entstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Hunderter mehr ...&lt;br /&gt;
           rjmp  lcd_number_1     ; ... und ab zur nächsten Runde&lt;br /&gt;
;&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorherhgehende Schleife 100 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; abzählen wieviele Zehner in&lt;br /&gt;
                  ; der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_3:&lt;br /&gt;
           subi  temp2, 10        ; 10 abziehen&lt;br /&gt;
           brcs  lcd_number_4     ; ist dadurch ein Unterlauf enstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Zehner mehr ...&lt;br /&gt;
           rjmp  lcd_number_3     ; ... und ab zur nächsten Runde&lt;br /&gt;
&lt;br /&gt;
                              ; die Zehnerstelle ausgeben&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; die übrig gebliebenen Einer&lt;br /&gt;
                  ; noch ausgeben&lt;br /&gt;
           ldi   temp1, &#039;0&#039;       ; die Zahl in temp2 ist jetzt im Bereich&lt;br /&gt;
           add   temp1, temp2     ; 0 bis 9. Einfach nur den ASCII Code für&lt;br /&gt;
           rcall lcd_data         ; &#039;0&#039; dazu addieren und wir erhalten dierekt&lt;br /&gt;
                                  ; den ASCII Code für die Ziffer&lt;br /&gt;
&lt;br /&gt;
           pop   temp2            ; den gesicherten Inhalt von temp2 wieder herstellen&lt;br /&gt;
           ret                    ; und zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beachte: Diese Funktion benutzt wiederrum die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039;. Anders als bei den bisherigen Aufrufen ist &#039;&#039;&#039;lcd_number&#039;&#039;&#039; aber darauf angewiesen, dass &#039;&#039;&#039;lcd_data&#039;&#039;&#039; das Register &#039;&#039;&#039;temp2&#039;&#039;&#039; unangetastet lässt. Falls sie es noch nicht getan haben, dann ist das jetzt die perfekte Gelegenheit, &#039;&#039;&#039;lcd_data&#039;&#039;&#039; mit den entsprechenden &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; Befehlen zu versehen. Sie sollten dies unbedingt zur Übung selbst machen. Am Ende muß die Funktion dann wie diese hier aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1           ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap  temp1                  ; Vertauschen&lt;br /&gt;
           andi  temp1, 0b00001111      ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr   temp1, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp1        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi  temp2, 0b00001111      ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr   temp2, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp2        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur ohne RS zu setzen&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           andi  temp1, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi  temp2, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz zur Funktionsweise der Funktion &#039;&#039;&#039;lcd_number&#039;&#039;&#039;: Die Zahl in einem Register bewegt sich im Wertebereich 0 bis 255. Um herauszufinden, wie die Hunderterstelle lautet, zieht die Funktion einfach in einer Schleife immer wieder 100 von der Schleife ab, bis bei der Subtraktion ein Unterlauf, angezeigt durch das Setzen des Carry-Bits bei der Subtraktion, entsteht. Die Anzahl wird im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; mitgezählt. Da dieses Register mit dem ASCII Code von &#039;0&#039; initialisiert wurde, und dieser ASCII Code bei jedem Schleifendurchlauf um 1 erhöht wird, können wir das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; direkt zur Ausgabe des Zeichens für die Hunderterstelle durch die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; benutzen. Völlig analog funktioniert auch die Ausgabe der Zehnerstelle.&lt;br /&gt;
&lt;br /&gt;
===Unterdrückung von führenden Nullen===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung: Diese Routine ist fehlerhaft&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt jede Zahl im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; immer mit 3 Stellen aus. Führende Nullen werden nicht unterdrückt. Möchte man dies ändern, so ist das ganz leicht möglich: Vor Ausgabe der Hunderterstelle bzw. Zehnerstelle muss lediglich überprüft werden, ob die Entsprechende Ausgabe eine &#039;0&#039; wäre. Ist sie das, so wird die Ausgabe übersprungen. Lediglich in der Einerstelle wird jede Ziffer wie errechnet ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ...&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben, wenn&lt;br /&gt;
                                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_2a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_2a:&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&lt;br /&gt;
           ...&lt;br /&gt;
                              ; die Zehnerstelle ausgeben, wenn&lt;br /&gt;
                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_4a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_4a:&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Verfahren, die einzelnen Stellen durch Subtraktion zu bestimmen, ist bei kleinen Zahlen eine durchaus gängige Alternative. Vor allem dann, wenn keine hardwaremäßige Unterstützung für Multiplikation und Division zur Verfügung steht. Ansonsten könnte man die die einzelnen Ziffern auch durch Division bestimmen. Das Prinzip ist folgendes (beispielhaft an der Zahl 52783 gezeigt)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   52783 / 10          -&amp;gt; 5278&lt;br /&gt;
   52783 - 5278 * 10   -&amp;gt;          3&lt;br /&gt;
&lt;br /&gt;
   5278 / 10           -&amp;gt; 527&lt;br /&gt;
   5278 - 527 * 10     -&amp;gt;          8&lt;br /&gt;
&lt;br /&gt;
   527 / 10            -&amp;gt; 52&lt;br /&gt;
   527 - 52 * 10       -&amp;gt;          7&lt;br /&gt;
&lt;br /&gt;
   52 / 10             -&amp;gt; 5&lt;br /&gt;
   52 - 5 * 10         -&amp;gt;          2&lt;br /&gt;
&lt;br /&gt;
   5 / 10              -&amp;gt; 0&lt;br /&gt;
   5 - 0 * 10          -&amp;gt;          5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Prinzip ist also die Restbildung bei einer fortgesetzten Division durch 10, wobei die einzelnen Ziffern in umgekehrter Reihenfolge ihrer Wertigkeit entstehen. Dadurch hat man aber ein Problem: Damit die Zeichen in der richtigen Reihenfolge ausgegeben werden können, muß man sie meistens zwischenspeichern um sie in der richtigen Reihenfole ausgeben zu können. Wird die Zahl in einem Feld von immer gleicher Größe ausgegeben, dann kann man auch die Zahl von rechts nach links ausgeben (bei einem LCD ist das möglich).&lt;br /&gt;
&lt;br /&gt;
===Hexadezimal ausgeben===&lt;br /&gt;
&lt;br /&gt;
Zu guter letzt hier noch eine Funktion, die eine Zahl aus dem Register &#039;&#039;&#039;temp1&#039;&#039;&#039; in hexadezimaler Form ausgibt. Die Funktion weist keine Besonderheiten auf und sollte unmittelbar verständlich sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen hexadezimal ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number_hex:&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           rcall lcd_number_hex_digit&lt;br /&gt;
           swap  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_number_hex_digit:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
           andi  temp1, $0F&lt;br /&gt;
           cpi   temp1, 10&lt;br /&gt;
           brlt  lcd_number_hex_digit_1&lt;br /&gt;
           subi  temp1, -( &#039;A&#039; - &#039;9&#039; - 1 ) ; es wird subi mit negativer Konstante verwendet, weil es kein addi gibt&lt;br /&gt;
lcd_number_hex_digit_1:&lt;br /&gt;
           subi  temp1, -&#039;0&#039;               ; ditto&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           &lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Eine 16-Bit Zahl aus einem Registerpärchen ausgeben===&lt;br /&gt;
&lt;br /&gt;
Um eine 16 Bit Zahl auszugeben wird wieder das bewährte Schema benutzt die einzelnen Stellen durch Subtraktion abzuzählen. Da es sich hierbei allerdings um eine 16 Bit Zahl handelt, müssen die Subtraktionen als 16-Bit Arithmetik ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 16 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp2 (low Byte) / temp3 (high Byte)&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number16:&lt;br /&gt;
           push  temp1&lt;br /&gt;
           push  temp2&lt;br /&gt;
           push  temp3&lt;br /&gt;
&lt;br /&gt;
; die Zehntausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number0:&lt;br /&gt;
           subi  temp2, low(10000)&lt;br /&gt;
           sbci  temp3, high(10000)&lt;br /&gt;
           brcs  lcd_number1&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number0&lt;br /&gt;
&lt;br /&gt;
; .. und ausgeben&lt;br /&gt;
lcd_number1:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-10000)&lt;br /&gt;
           sbci  temp3, high(-10000)&lt;br /&gt;
&lt;br /&gt;
; die Tausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number2:&lt;br /&gt;
           subi  temp2, low(1000)&lt;br /&gt;
           sbci  temp3, high(1000)&lt;br /&gt;
           brcs  lcd_number3&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number2&lt;br /&gt;
&lt;br /&gt;
; ... und ausgeben&lt;br /&gt;
lcd_number3:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-1000)&lt;br /&gt;
           sbci  temp3, high(-1000)&lt;br /&gt;
&lt;br /&gt;
; Als nächtes kommt die Hunderterstelle drann&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number4:&lt;br /&gt;
           subi  temp2, low(100)&lt;br /&gt;
           sbci  temp3, high(100)&lt;br /&gt;
           brcs  lcd_number5&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number4&lt;br /&gt;
&lt;br /&gt;
; und ausgeben&lt;br /&gt;
lcd_number5:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100&lt;br /&gt;
&lt;br /&gt;
; bleiben noch die Zehner&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number6:&lt;br /&gt;
           subi  temp2, 10&lt;br /&gt;
           brcs  lcd_number7&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number6&lt;br /&gt;
&lt;br /&gt;
; ausgeben ...&lt;br /&gt;
lcd_number7:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10&lt;br /&gt;
&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
           add   temp1, temp2&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
; fertig. Stack wieder aufräumen&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           pop   temp3&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Der überarbeitete, komplette Code==&lt;br /&gt;
&lt;br /&gt;
Hier also die komplett überarbeitete Version der LCD Funktionen.&lt;br /&gt;
&lt;br /&gt;
Die für die Benutzung relevanten Funktionen&lt;br /&gt;
* &#039;&#039;&#039;lcd_init&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_clear&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_home&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_data&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_flash_string&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number_hex&#039;&#039;&#039;&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; übergeben, wobei &#039;&#039;&#039;temp1&#039;&#039;&#039; vom Usercode definiert werden muss.&lt;br /&gt;
&lt;br /&gt;
[[Media:lcd-routines.asm|Download lcd-routines.asm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Stack|&lt;br /&gt;
zurücklink=AVR-Tutorial: Stack|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Interrupts|&lt;br /&gt;
vorlink=AVR-Tutorial: Interrupts}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister SREG) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register temp1 übergeben, wobei temp1 vom Usercode definiert werden muss. &lt;br /&gt;
&lt;br /&gt;
der progger hat vergessen das Z reg in den sub&#039;s lcd_flash_string ...&lt;br /&gt;
zu sichern, aber danke für den Einstig&lt;br /&gt;
&lt;br /&gt;
Lars&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27999</id>
		<title>AVR-Tutorial: LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27999"/>
		<updated>2008-05-16T03:21:04Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Welche Befehle versteht das LCD? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kaum ein elektronisches Gerät kommt heutzutage noch ohne ein LCD daher. Ist doch auch praktisch, Informationen im Klartext anzeigen zu können, ohne irgendwelche LEDs blinken zu lassen. Kein Wunder also, dass die häufigste Frage in Mikrocontroller-Foren ist: &amp;quot;Wie kann ich ein LCD anschließen?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-Bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-Bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-Bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-Bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-Bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-Bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4 Bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert. Ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitet hat. Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt, da wir wissen, welche Anschlüsse das LCD benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Pinnummer&amp;lt;BR&amp;gt;LCD || Bezeichnung || Anschluss&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |1 || Vss || GND&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |2 || Vcc || 5V&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |3 || Vee || GND oder [[Potentiometer | Poti]] &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |4 || RS || PD4 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |5 || RW || GND &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |6 || E || PD5 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |7 || DB0 || nicht angeschlossen &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |8 || DB1 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |9 || DB2 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |10 || DB3 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |11 || DB4 || PD0 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |12 || DB5 || PD1 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |13 || DB6 || PD2 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |14 || DB7 || PD3 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |15 || A || Vorsicht! Meistens nicht direkt an +5V anschließbar,&amp;lt;BR&amp;gt;nur über einen Vorwiderstand (z.B. 33&amp;amp;Omega;)!&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |16 || K || GND&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Ok alles ist verbunden. Wenn man jetzt den Strom einschaltet, sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display?&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung des LCDs im 4-Bit-Modus ==&lt;br /&gt;
&lt;br /&gt;
Um ein Byte zu übertragen, muss man es erstmal in die beiden Nibbles zerlegen, die getrennt übertragen werden. Da das obere Nibble (Bit 4 - Bit 7) als erstes übertragen wird, die 4 Datenleitungen jedoch an die vier unteren Bits des Port D angeschlossen sind, muss man die beiden Nibbles des zu übertragenden Bytes erstmal vertauschen. Der AVR kennt dazu praktischerweise einen eigenen Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           swap r16               ; vertauscht die beiden Nibbles von r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus 0b00100101 wird so z.B. 0b01010010. &lt;br /&gt;
&lt;br /&gt;
Jetzt sind die Bits für die erste Phase der Übertragung an der richtigen Stelle. Trotzdem wollen wir das Ergebnis nicht einfach so mit &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; an den Port geben. Um die Hälfte des Bytes, die jetzt nicht an die Datenleitungen des LCDs gegeben wird auf null zu setzen, verwendet man folgenden Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           andi r16, 0b00001111   ; Nur die vier unteren (mit 1 markierten)&lt;br /&gt;
                                  ; Bits werden übernommen, alle anderen werden null&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also: Das obere Nibble wird erst mit dem unteren vertauscht, damit es unten ist. Dann wird das obere (das wir jetzt noch nicht brauchen) auf null gesetzt. &lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem LCD noch mitteilen, ob wir Daten oder Befehle senden wollen. Das machen wir, indem wir das Bit, an dem RS angeschlossen ist (PD4), auf 0 (Befehl senden) oder auf 1 (Daten senden) setzen. Um ein Bit in einem normalen Register zu setzen, gibt es den Befehl sbr (Set Bit in Register). Dieser Befehl unterscheidet sich jedoch von sbi (das nur für IO-Register gilt) dadurch, dass man nicht die Nummer des zu setzenden Bits angibt, sondern eine Bitmaske. Das geht so: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbr r16, 0b00010000     ; Bit 4 setzen, alle anderen Bits bleiben gleich&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RS ist an PD4 angeschlossen. Wenn wir r16 an den Port D ausgeben, ist RS jetzt also high und das LCD erwartet Daten anstatt von Befehlen. &lt;br /&gt;
&lt;br /&gt;
Das Ergebnis können wir jetzt endlich direkt an den Port D übergeben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           out PORTD, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss vorher der Port D auf Ausgang geschalten werden, indem man 0xFF ins Datenrichtungsregister DDRD schreibt. &lt;br /&gt;
&lt;br /&gt;
Um dem LCD zu signalisieren, dass es das an den Datenleitungen anliegende Nibble übernehmen kann, wird die E-Leitung (Enable, an PD5 angeschlossen) auf high und kurz darauf wieder auf low gesetzt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbi PORTD, 5              ; Enable high&lt;br /&gt;
           nop                       ; 3 Taktzyklen warten (&amp;quot;nop&amp;quot; = nichts tun)&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5              ; Enable wieder low&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die eine Hälfte des Bytes wäre damit geschafft! Die andere Hälfte kommt direkt hinterher: Alles, was an der obenstehenden Vorgehensweise geändert werden muss, ist, das &amp;quot;swap&amp;quot; (Vertauschen der beiden Nibbles) wegzulassen.&lt;br /&gt;
&lt;br /&gt;
== Initialisierung des Displays ==&lt;br /&gt;
&lt;br /&gt;
Allerdings gibt es noch ein Problem. Wenn ein LCD eingeschaltet wird, dann läuft es zunächst im 8 Bit Modus. Irgendwie muss das Display initialisiert und auf den 4 Bit Modus umgeschaltet werden, und zwar nur mit den 4 zur Verfügung stehenden Datenleitungen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn es Probleme gibt, dann meistens an diesem Punkt. Die &amp;quot;kompatiblen&amp;quot; Kontroller sind gelegentlich doch nicht 100% identisch. Es lohnt sich, das Datenblatt (siehe Weblinks im Artikel [[LCD]]) genau zu lesen, in welcher Reihenfolge und mit welchen Abständen (Delays) die Initialisierungbefehle gesendet werden. Eine weitere Hilfe können Ansteuerungsbeispiele in Forenbeiträgen geben z.B.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79609#664268 (A) KS0066U oder Ähnliche --- LCD Treiber]&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 4 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Achtung: Im Folgenden sind alle Bytes aus Sicht des LCD-Kontrollers angegeben! Da LCD-seitig nur die Leitungen DB4 - DB7 verwendet werden, ist daher immer nur das höherwertige Nibble gültig. Durch die Art der Verschaltung (DB4 - DB7 wurde auf dem PORT an PD0 bis PD3 angeschlossen) ergibt sich eine Verschiebung, so dass das am Kontroller auszugebende Byte nibblemässig vertauscht ist!&lt;br /&gt;
&lt;br /&gt;
Die Sequenz, aus Sicht des Kontrollers, sieht so aus:&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* $2 ins Steuerregister schreiben (RS = 0), dadurch wird auf 4 Bit Daten umstellt&lt;br /&gt;
* Ab jetzt muss für die Übertragung eines Bytes jeweils zuerst das höherwertige Nibble und dann das niederwertige Nibble übertragen werden, wie oben beschrieben&lt;br /&gt;
* Mit dem Konfigurier-Befehl $20 das Display konfigurieren (4-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 8 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Der Vollständigkeit halber hier noch die notwendige Initialiserungssequenz für den 8 Bit Modus. Da hier die Daten komplett als 1 Byte übertragen werden können, sind einige Klimmzüge wie im 4 Bit Modus nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mit dem Konfigurier-Befehl 0x30 das Display konfigurieren (8-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
== Routinen zur LCD-Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
Die Routinen zur Kommunikation mit dem LCD sehen also so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000 (Anm.1)&lt;br /&gt;
           out PORTD, temp1             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000&lt;br /&gt;
           out PORTD, temp2             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out PORTD, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
 ;&lt;br /&gt;
 ; Bei höherem Takt (&amp;gt;= 8 MHz) kann es notwendig sein, &lt;br /&gt;
 ; vor dem Enable High 1-2 Wartetakte (nop) einzufügen. &lt;br /&gt;
 ; Siehe dazu http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi PORTD, 5                 ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5                 ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
&lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi  temp3,50&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall  delay5ms&lt;br /&gt;
           dec  temp3&lt;br /&gt;
           brne powerupwait&lt;br /&gt;
           ldi temp1, 0b00000011        ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out PORTD, temp1             ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00000010        ; 4bit-Modus einstellen&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00101000        ; 4Bit / 2 Zeilen / 5x8&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00001100        ; Display ein / Cursor aus / kein Blinken&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00000100        ; inkrement / kein Scrollen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi temp1, 0b00000001   ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi temp1, 0b00000010   ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anm.1: Siehe [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
Weitere Funktionen (wie z.B. Cursorposition verändern) sollten mit Hilfe der [[AVR-Tutorial:_LCD#Welche_Befehle_versteht_das_LCD.3F|Befehlscodeliste]] nicht schwer zu realisieren sein. Einfach den Code in temp laden, lcd_command aufrufen und ggf. eine Pause einfügen. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man die LCD-Ansteuerung auch an einen anderen Port des Mikrocontrollers &amp;quot;verschieben&amp;quot;: Wenn das LCD z.B. an Port B angeschlossen ist, dann reicht es, im Programm alle &amp;quot;PORTD&amp;quot; durch &amp;quot;PORTB&amp;quot; und &amp;quot;DDRD&amp;quot; durch &amp;quot;DDRB&amp;quot; zu ersetzen. &lt;br /&gt;
&lt;br /&gt;
Wer eine höhere Taktfrequenz als 4 MHz verwendet, der sollte daran denken, die Dauer der Verzögerungsschleifen anzupassen.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das diese Routinen zur Anzeige von Text verwendet, kann z.B. so aussehen (die Datei lcd-routines.asm muss sich im gleichen Verzeichnis befinden). Nach der Initialisierung wird zuerst der Displayinhalt gelöscht. Um dem LCD ein Zeichen zu schicken, lädt man es in temp1 und ruft die Routine &amp;quot;lcd_data&amp;quot; auf. Das folgende Beispiel zeigt das Wort &amp;quot;Test&amp;quot; auf dem LCD an. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/lcd-test.asm Download lcd-test.asm] &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;
&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, 0xFF    ; Port D = Ausgang&lt;br /&gt;
           out DDRD, temp1&lt;br /&gt;
&lt;br /&gt;
           rcall lcd_init     ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear    ; Display löschen&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;T&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;e&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           &lt;br /&gt;
           ldi temp1, &#039;s&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;t&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD-Routinen werden hier eingefügt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für längere Texte ist die Methode, jedes Zeichen einzeln in das Register zu laden und &amp;quot;lcd_data&amp;quot; aufzurufen natürlich nicht sehr praktisch. Dazu später aber mehr.&lt;br /&gt;
&lt;br /&gt;
Bisher wurden in Register immer irgendwelche Zahlenwerte geladen, aber in diesem Programm kommt plötzlich die Anweisung&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
vor. Wie ist diese zu verstehen? Passiert hier etwas grundlegend anderes als beim Laden einer Zahl in ein Register?&lt;br /&gt;
&lt;br /&gt;
Die Antwort darauf lautet: Nein. Auch hier wird letztendlich nur eine Zahl in ein Register geladen. Der Schlüssel zum Verständnis beruht darauf, dass zum LCD, so wie zu allen Ausgabegeräten, für die Ausgabe von Texten immer nur Zahlen übertragen werden, sog. Codes. Zum Beispiel könnte man vereinbaren, dass ein LCD, wenn es den Ausgabecode 65 erhält, ein &#039;A&#039; anzeigt, bei einem Ausgabecode von 66 ein &#039;B&#039; usw. Naturgemäß gibt es daher viele verschiedene Code-Buchstaben Zuordnungen. Damit hier etwas Ordnung in das potentielle Chaos kommt, hat man sich bereits in der Steinzeit der Programmierung auf bestimmte Codetabellen geeinigt, von denen die verbreitetste sicherlich die ASCII-Zuordnung ist.&lt;br /&gt;
&lt;br /&gt;
==ASCII==&lt;br /&gt;
&lt;br /&gt;
ASCII steht für &#039;&#039;American Standard Code for Information Interchange&#039;&#039; und ist ein standardisierter Code zur Zeichenumsetzung. Die Codetabelle sieht hexadezimal dabei wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;.0&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x1&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x2&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x3&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x4&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x6&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x7&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x8&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x9&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xA&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xB&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xC&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xD&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xE&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xF&amp;lt;/th&amp;gt;&amp;lt;br&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;&#039;&#039;&#039;0x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NUL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SOH&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;STX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EOT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ENQ&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ACK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BEL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;HT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;VT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CR&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SO&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SI&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;&#039;&#039;&#039;1x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DLE&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NAK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SYN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CAN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EM&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SUB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ESC&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;US&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;&#039;&#039;&#039;2x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SP&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;!&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;#&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;%&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;amp;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;(&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;)&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;*&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;+&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;,&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;.&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;3x&#039;&#039;&#039;&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;&amp;lt;td&amp;gt;3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;9&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;=&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;gt;&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;4x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;B&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;C&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;D&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;F&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;G&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;H&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;I&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;J&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;L&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;M&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;N&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;O&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;&#039;&#039;&#039;5x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;P&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;R&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;S&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;T&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;U&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;V&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;W&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;X&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;[&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;\&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;]&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;^&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;6x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;`&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;a&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;b&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;c&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;d&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;e&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;f&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;g&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;h&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;i&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;j&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;k&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;l&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;m&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;n&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;o&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;&#039;&#039;&#039;7x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;p&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;r&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;s&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;t&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;u&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;v&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;w&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;{&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;|&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;~&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DEL&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 ersten beiden Zeilen enthalten die Codes für einige Steuerzeichen, ihre vollständige Beschreibung würde hier zu weit führen. Das Zeichen &#039;&#039;&#039;SP&#039;&#039;&#039; steht für ein &#039;&#039;Space&#039;&#039;, also ein Leerzeichen. &#039;&#039;&#039;BS&#039;&#039;&#039; steht für &#039;&#039;Backspace&#039;&#039;, also ein Zeichen zurück. &#039;&#039;&#039;DEL&#039;&#039;&#039; steht für &#039;&#039;Delete&#039;&#039;, also das Löschen eines Zeichens. &#039;&#039;&#039;CR&#039;&#039;&#039; steht für &#039;&#039;Carriage Return&#039;&#039;, also wörtlich: der Wagenrücklauf (einer Schreibmaschine), während &#039;&#039;&#039;LF&#039;&#039;&#039; für &#039;&#039;Line feed&#039;&#039;, also einen Zeilenvorschub steht.&lt;br /&gt;
&lt;br /&gt;
Der Assembler kennt diese Codetabelle und ersetzt die Zeile&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, $54&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
was letztendlich auch der Lesbarkeit des Programmes zugute kommt. Funktional besteht kein Unterschied zwischen den beiden Anweisungen. Beide bewirken, dass das Register temp1 mit dem Bitmuster 01010100 ( = hexadezimal 54, = dezimal 84 oder eben der ASCII Code für &#039;&#039;&#039;T&#039;&#039;&#039;) geladen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das LCD wiederrum kennt diese Code-Tabelle ebenfalls und wenn es über den Datenbus die Codezahl $54 zur Anzeige empfängt, dann schreibt es ein &#039;&#039;&#039;T&#039;&#039;&#039; an die aktuelle Cursorposition. Genauer gesagt, weiss das LCD nichts von einem &#039;&#039;&#039;T&#039;&#039;&#039;. Es sieht einfach in seinen internen Tabellen nach, welche Pixel beim Empfang der Codezahl $54 auf schwarz zu setzen sind. &#039;Zufällig&#039; sind das genau jene Pixel, die für uns Menschen ein &#039;&#039;&#039;T&#039;&#039;&#039; ergeben.&lt;br /&gt;
&lt;br /&gt;
==Welche Befehle versteht das LCD?==&lt;br /&gt;
&lt;br /&gt;
Auf dem LCD arbeitet ein Kontroller vom Typ HD44780. Dieser Kontroller versteht eine Reihe von Befehlen, die allesamt mittels lcd_command gesendet werden können. Ein Kommando ist dabei nichts anderes als ein Befehlsbyte, in dem die verschiedenen Bits verschiedene Bedeutungen haben:&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 0 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 1 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;der Zustand dieses Bits ist egal&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;sonstige Buchstaben&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;das Bit muss je nach gewünschter Funktionalität gesetzt werden. Die mögliche Funktionalität des jeweiligen Bits geht aus der Befehlsbeschreibung hervor&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Kommando &#039;ON/OFF Control&#039; soll benutzt werden, um das Display einzuschalten, der Cursor soll eingeschaltet werden und der Cursor soll blinken.&lt;br /&gt;
Das Befehlsbyte ist so aufgebaut:&lt;br /&gt;
   0b00001dcb&lt;br /&gt;
Aus der Befehlsbeschreibung entnimmt man:&lt;br /&gt;
* Display ein bedeutet, dass an der Bitposition d eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor ein bedeutet, dass an der Bitposition c ein 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor blinken bedeutet, dass an der Bitposition b eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
Das dafür zu übertragende Befehlsbyte hat also die Gestalt 0b00001111 oder in hexadezimaler Schreibweise $0F.&lt;br /&gt;
&lt;br /&gt;
===Clear display: 0b00000001===&lt;br /&gt;
&lt;br /&gt;
Die Anzeige wird gelöscht und der Ausgabecursor kehrt an die Home Position (links, erste Zeile) zurück.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Cursor home: 0b0000001x===&lt;br /&gt;
&lt;br /&gt;
Der Cursor kehrt an die Home Position (links, erste Zeile) zurück. Ein verschobenes Display wird auf die Grundeinstellung zurückgesetzt.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs bis 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Entry mode: 0b000001is===&lt;br /&gt;
&lt;br /&gt;
Legt die Cursor Richtung sowie eine mögliche Verschiebung des Displays fest&lt;br /&gt;
* i = 1, Cursorposition bei Ausgabe eines Zeichens erhöhen&lt;br /&gt;
* i = 0, Cursorposition bei Ausgabe eines Zeichens vermindern&lt;br /&gt;
* s = 1, Display wird gescrollt, wenn der Cursor das Ende/Anfang, je nach Einstellung von i, erreicht hat.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===On/off control: 0b00001dcb===&lt;br /&gt;
&lt;br /&gt;
Display insgesamt ein/ausschalten; den Cursor ein/ausschalten; den Cursor auf blinken schalten/blinken aus. Wenn das Display ausgeschaltet wird, geht der Inhalt des Displays nicht verloren. Der vorher angezeigte Text wird nach wiedereinschalten erneut angezeigt.&lt;br /&gt;
Ist der Cursor eingeschaltet, aber Blinken ausgeschaltet, so wird der Cursor als Cursorzeile in Pixelzeile 8 dargestellt. Ist Blinken eingeschaltet, wird der Cursor als blinkendes ausgefülltes Rechteck dargestellt, welches abwechselnd mit dem Buchstaben an dieser Stelle angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
* d = 0, Display aus&lt;br /&gt;
* d = 1, Display ein&lt;br /&gt;
* c = 0, Cursor aus&lt;br /&gt;
* c = 1, Cursor ein&lt;br /&gt;
* b = 0, Cursor blinken aus&lt;br /&gt;
* b = 1, Cursor blinken ein&lt;br /&gt;
 &lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Cursor/Scrollen: 0b0001srxx===&lt;br /&gt;
&lt;br /&gt;
Bewegt den Cursor oder scrollt das Display um eine Position entweder nach rechts oder nach links.&lt;br /&gt;
&lt;br /&gt;
* s = 1, Display scrollen&lt;br /&gt;
* s = 0, Cursor bewegen&lt;br /&gt;
* r = 1, nach rechts&lt;br /&gt;
* r = 0, nach links &lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Konfiguration: 0b001dnfxx===&lt;br /&gt;
&lt;br /&gt;
Einstellen der Interface Art, Modus, Font&lt;br /&gt;
* d = 0, 4-Bit Interface&lt;br /&gt;
* d = 1, 8-Bit Interface&lt;br /&gt;
* n = 0, 1 zeilig&lt;br /&gt;
* n = 1, 2 zeilig&lt;br /&gt;
* f = 0, 5x7 Pixel&lt;br /&gt;
* f = 1, 5x11 Pixel&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Character RAM Address Set: 0b01aaaaaa===&lt;br /&gt;
&lt;br /&gt;
Mit diesem Kommando werden maximal 8 selbst definierte Zeichen definiert. Dazu wird der Character RAM Zeiger auf den Anfang des Character Generator (CG) RAM gesetzt und das Zeichen durch die Ausgabe von 8 Byte definiert. Der Adresszeiger wird nach Ausgabe jeder Pixelzeile (8 Bit) vom LCD selbst erhöht. Nach Beendigung der Zeichendefinition muss die Schreibposition explizit mit dem Kommando &amp;quot;Display RAM Address Set&amp;quot; wieder in den DD-RAM Bereich gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
aaaaaa 6-bit CG RAM Adresse&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Display RAM Address Set: 0b1aaaaaaa===&lt;br /&gt;
&lt;br /&gt;
Den Cursor neu positionieren. Display Data (DD) Ram ist vom Character Generator (CG) Ram unabhängig. Der Adresszeiger wird bei Ausgabe eines Zeichens ins DD Ram automatisch erhöht. Das Display verhält sich so, als ob eine Zeile immer aus 32 logischen Zeichen besteht, von der, je nach konkretem Displaytyp (16 Zeichen, 20 Zeichen) immer nur ein Teil sichtbar ist.&lt;br /&gt;
&lt;br /&gt;
aaaaaaa 7-bit DD RAM Adresse. Auf 2-zeiligen Displays (und den meisten 16x1 Displays), kann die Adressangabe wie folgt interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
1laaaaaa&lt;br /&gt;
* l = Zeilennummer (0 oder 1)&lt;br /&gt;
* a = 6-Bit Spaltennummer&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
==Einschub: Code aufräumen==&lt;br /&gt;
&lt;br /&gt;
Es wird Zeit sich einmal etwas kritisch mit den bisher geschriebenen Funktionen auseinander zu setzen.&lt;br /&gt;
&lt;br /&gt;
===Portnamen aus dem Code herausziehen===&lt;br /&gt;
&lt;br /&gt;
Wenn wir die LCD-Funktionen einmal genauer betrachten, dann fällt sofort auf, daß über die Funktionen verstreut immer wieder das &#039;&#039;&#039;PORTD&#039;&#039;&#039; sowie einzelne Zahlen für die Pins an diesem Port auftauchen. Wenn das LCD an einem anderen Port betrieben werden soll, oder sich die Pin-Belegung ändert, dann muß an all diesen Stellen eine Anpassung vorgenommen werden. Dabei darf keine einzige Stelle übersehen werden, ansonsten würden die LCD-Funktionen nicht oder nicht vollständig funktionieren.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit dem vorzubeugen ist es, diese immer gleichbleibenden Dinge an den Anfang der LCD-Funktionen vorzuziehen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
.equ PIN_E    = 5&lt;br /&gt;
.equ PIN_RS   = 4&lt;br /&gt;
&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp1          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp2          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi LCD_PORT, PIN_E          ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi LCD_PORT, PIN_E          ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
 &lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf Ausgang&lt;br /&gt;
           out   LCD_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi   temp3,6&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           dec   temp3&lt;br /&gt;
           brne  powerupwait&lt;br /&gt;
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out   LCD_PORT, temp1        ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00101000      ; 4 Bot, 2 Zeilen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00001100      ; Display on, Cursor off&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00000100      ; endlich fertig&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi   temp1, 0b00000010      ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels &#039;&#039;&#039;.equ&#039;&#039;&#039; werden mit dem Assembler Textersetzungen vereinbart. Der Assembler ersetzt alle Vorkomnisse des Quelltextes durch den zu ersetzenden Text. Dadurch ist es zb. möglich alle Vorkommnisse von &#039;&#039;&#039;PORTD&#039;&#039;&#039; durch &#039;&#039;&#039;LCD_PORT&#039;&#039;&#039; auszutauschen. Wird das LCD an einen anderen Port, zb. &#039;&#039;&#039;PORTB&#039;&#039;&#039; gelegt, dann genügt es, einzig und alleine die Zeilen&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTB&lt;br /&gt;
.equ LCD_DDR  = DDRB&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu ersetzen. Der Assembler sorgt dann dafür, dass diese Portänderung an den relevanten Stellen im Code über die Textersetzungen einfliesst. Selbiges natürlich mit der Pin-Zuordnung.&lt;br /&gt;
&lt;br /&gt;
===Registerbenutzung===&lt;br /&gt;
&lt;br /&gt;
Bei diesen Funktionen mussten einige Register des Prozessors benutzt werden um darin Zwischenergebnisse zu speichern bzw. zu bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss dabei natürlich, dass es zu keinen Überschneidungen kommt. Solange nur jede Funktion jeweils für sich betrachtet wird, ist das kein Problem. In 20 oder 30 Code-Zeilen kann man gut verfolgen, welches Register wofür benutzt wird. Schwieriger wird es, wenn Funktionen wiederrum andere Funktionen aufrufen, die ihrerseits wieder Funktionen aufrufen usw. Jede dieser Funktionen benutzt einige Register und mit zunehmender Programmgröße wird es immer schwieriger zu verfolgen, welches Register zu welchem Zeitpunkt wofür benutzt wird.&lt;br /&gt;
&lt;br /&gt;
Speziell bei Basisfunktionen, wie diesen LCD-Funktionen, ist es daher oft ratsam, dafür zu sorgen, daß jede Funktion die Register wieder in dem Zustand hinterlässt, indem sie sie auch vorgefunden hat. Wir benötigen dazu wieder den Stack, auf dem die Registerinhalte bei Betreten einer Funktion zwischengespeichert werden und von dem die Register bei Verlassen einer Funktion wiederhergestellt werden.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir die Funktion&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion verändert das Register temp1. Um das Register abzusichern schreiben wir die Funktion um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           push  temp1                  ; temp1 auf dem Stack sichern&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           pop   temp1                  ; temp1 vom Stack wiederherstellen&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am besten hält man sich an die Regel: Jede Funktion ist dafür zuständig die Register zu sichern und wiederherzustellen, die sie auch selbst verändert. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; ruft die Funktionen &#039;&#039;&#039;lcd_command&#039;&#039;&#039; und &#039;&#039;&#039;delay5ms&#039;&#039;&#039; auf. Wenn diese Funktionen selbst wieder Register verändern (und das tun sie), so ist es die Aufgabe dieser Funktionen, sich um die Sicherung und das Wiederherstellen der entsprechenden Register zu kümmern. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; sollte sich nicht darum kümmern müssen. Auf diese Weise ist das Schlimmste, das einem passieren kann, das ein paar Register unnütz gesichert und wiederhergestellt werden. Das kostet zwar etwas Rechenzeit und etwas Speicherplatz auf dem Stack, ist aber immer noch besser als das andere Extrem: Nach einem Funktionsaufruf haben einige Register nicht mehr den Wert den sie haben sollten und das Programm rechnet mit falschen Zahlen weiter.&lt;br /&gt;
&lt;br /&gt;
===Lass den Assembler rechnen===&lt;br /&gt;
Betrachtet man den Code genauer, so fallen einige konstante Zahlenwerte auf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Das vorangestellte $ kennzeichnet die Zahl als Hexadezimalzahl) Der Code benötigt eine Warteschleife die mindestens 50µs dauert. Die beiden Befehle innerhalb der Schleife benötigen 3 Takte: 1 Takt für den &#039;&#039;&#039;dec&#039;&#039;&#039; und der &#039;&#039;&#039;brne&#039;&#039;&#039; benötigt 2 Takte wenn die Bedingung zutrifft, der Branch also genommen wird. Bei 4 Mhz werden also 4000000 / 3 * 50 / 1000000 = 66.6 Durchläufe durch die Schleife benötigt um eine Verzögerungszeit von 50µs (0.000050 Sekunden) zu erreichen, hexadezimal ausgedrückt: $42&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist: Bei anderen Taktfrequenzen müsste man nun jedesmal diese Berechnung machen und den entsprechenden Zahlenwert einsetzen. Das kann aber der Assembler genausogut erledigen. Am Anfang des Codes wird ein Eintrag definiert, der die Taktfrequenz festlegt. Traditionell heist dieser Eintrag &amp;lt;i&amp;gt;XTAL&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ XTAL  = 4000000&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 50 / 3 ) / 1000000&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An einer anderen Codestelle gibt es weitere derartige magische Zahlen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was geht hier vor?&lt;br /&gt;
Die innere Schleife benötigt wieder 3 Takte pro Durchlauf. Bei $C9 = 201 Durchläufen werden also 201 * 3 = 603 Takte verbraucht. In der äußeren Schleife kommen pro Durchlauf alo 1 + 603 + 1 + 2 = 607 Takte verbraucht. Da die äußere Schleife $21 = 33 mal wiederholt wird, werden 20031 Takte verbraucht. Bei 4Mhz benötigt der Prozessor 20031 / 4000000 = 0.005007 Sekunden, also 5 ms.&lt;br /&gt;
Wird der Wiederholwert für die innere Schleife bei $C9 belassen, so werden 4000000 / 607 * 5 / 1000 Wiederholungen der äusseren Schleife benötigt. Auch diese Berechnung kann wieder der Assembler übernehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 5 / 607 ) / 1000&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein kleines Problem kann bei der Verwendung dieses Verfahrens entstehen: Bei hohen Taktfrequenzen und großen Wartezeiten kann der berechnete Wert größer als 255 werden und man bekommt die Fehlermeldung &amp;quot;Operand(s) out of range&amp;quot; beim Assemblieren. Dieser Fall tritt zum Beispiel für obige Konstruktion bei einer Taktfrequenz von 16 MHz ein (genauer gesagt ab 15,3 MHz), während darunter XTAL beliebig geändert werden kann. Als einfachste Lösung bietet es sich an, die Zahl der Takte pro Schleifendurchlauf durch das Einfügen von &#039;&#039;&#039;nop&#039;&#039;&#039; zu erhöhen und die Berechnungsvorschrift anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Ausgabe eines konstanten Textes ==&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde schon einmal ein Text ausgegeben. Dies geschah durch Ausgabe von einzelnen Zeichen. Das können wir auch anders machen. Wir können den Text im Speicher ablegen und eine Funktion schreiben, die die einzelnen Zeichen aus dem Speicher holt und ausgibt. Dabei erhebt sich aber eine Fragestellung: Woher weiß die Funktion eigentlich, wie lange der Text ist? Die Antwort darauf lautet: Sie kann es nicht wissen. Wir müssen irgendwelche Vereinbarungen treffen, woran die Funktion erkennen kann, dass der Text zu Ende ist. Im Wesentlichen werden dazu 2 Methoden benutzt:&lt;br /&gt;
* Der Text enthält ein spezielles Zeichen, welches das Ende des Textes markiert&lt;br /&gt;
* Wir speichern nicht nur den Text selbst, sondern auch die Länge des Textes&lt;br /&gt;
Mit einer der beiden Methoden ist es der Textausgabefunktion dann ein Leichtes, den Text vollständig auszugeben.&lt;br /&gt;
&lt;br /&gt;
Wir werden uns im Weiteren dafür entscheiden, ein spezielles Zeichen, eine 0, dafür zu benutzen. Die Ausgabefunktionen werden dann etwas einfacher, als wenn bei der Ausgabe die Anzahl der bereits ausgegebenen Zeichen mitgezählt werden muss.&lt;br /&gt;
&lt;br /&gt;
Den Text selbst speichern wir im Flash-Speicher, also dort wo auch das Programm gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Einen konstanten Text aus dem Flash Speicher&lt;br /&gt;
 ; ausgeben. Der Text wird mit einer 0 beendet&lt;br /&gt;
lcd_flash_string:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_1:&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           rjmp  lcd_flash_string_1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_2:&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion benutzt den Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039; um das jeweils nächste Zeichen aus dem Flash Speicher in ein Register zur Weiterverarbeitung zu laden. Dazu wird der sog. &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; benutzt. So nennt man das Registerpaar &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;. Nach jedem Ladevorgang wird dabei durch den Befehl&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
dieser Z-Pointer um 1 erhöht. Mittels &#039;&#039;&#039;cpi&#039;&#039;&#039; wird das in das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; geladene Zeichen mit 0 verglichen. &#039;&#039;&#039;cpi&#039;&#039;&#039; vergleicht die beiden Zahlen und merkt sich das Ergebnis in einem speziellen Register in Form von Status Bits. &#039;&#039;&#039;cpi&#039;&#039;&#039; zieht dabei ganz einfach die beiden Zahlen voneinander ab. Sind sie gleich, so kommt da als Ergebnis 0 heraus und &#039;&#039;&#039;cpi&#039;&#039;&#039; setzt daher konsequenter Weise das Zero-Flag, das anzeigt, daß die vorhergegangene Operation ein 0 Ergebnis hatte.&#039;&#039;&#039;breq&#039;&#039;&#039; wertet diese Status-Bits aus. Wenn die vorhergegangene Operation ein 0-Ergebnis hatte, das Zero-Flag also gesetzt ist, dann wird ein Sprung zum angegebenen Label durchgeführt. In Summe bewirkt also die Sequenz&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
das das gelesene Zeichen mit 0 verglichen wird und falls das gelesene&lt;br /&gt;
Zeichen tatsächlich 0 war, an der Stelle lcd_flash_string_2 weiter gemacht wird. Im anderen Fall wird die bereits geschriebene Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aufgerufen, welche das Zeichen ausgibt. &#039;&#039;&#039;lcd_data&#039;&#039;&#039; erwartet dabei das Zeichen im Register &#039;&#039;&#039;temp1&#039;&#039;&#039;, genau in dem Register in welches wir vorher mittels &#039;&#039;&#039;lpm&#039;&#039;&#039; das Zeichen geladen hatten.&lt;br /&gt;
&lt;br /&gt;
Das verwendende Programm sieht dann 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 temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
           rcall lcd_init              ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear             ; Display löschen&lt;br /&gt;
 &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;
&lt;br /&gt;
           rcall lcd_flash_string      ; Unterprogramm gibt String aus der&lt;br /&gt;
                                       ; durch den Z-Pointer adressiert wird&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
           .db &amp;quot;Test&amp;quot;,0                ; Stringkonstante, durch eine 0&lt;br /&gt;
                                       ; abgeschlossen  &lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD Funktionen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Genaueres über die Verwendung unterschiedlicher Speicher findet sich im Kapitel [[AVR-Tutorial:_Speicher|Speicher]]&lt;br /&gt;
&lt;br /&gt;
==Zahlen ausgeben==&lt;br /&gt;
Um Zahlen, die beispielsweise in einem Register gespeichert sind, ausgeben zu können, ist es notwendig sich eine Textrepräsentierung der Zahl zu generieren. Die Zahl 123 wird also in den Text &amp;quot;123&amp;quot; umgewandelt welcher dann ausgegeben wird. Aus praktischen Gründen wird allerdings der Text nicht vollständig generiert (man müsste ihn ja irgendwo zwischenspeichern) sondern die einzelnen Buchstaben werden sofort ausgegeben, sobald sie bekannt sind.&lt;br /&gt;
&lt;br /&gt;
===Dezimal ausgeben===&lt;br /&gt;
Das Prinzip der Umwandlung ist einfach. Um herauszufinden wieviele Hunderter in der Zahl 123 enthalten sind, genügt es in einer Schleife immer wieder 100 von der Zahl abzuziehen und mitzuzählen wie oft dies gelang, bevor das Ergebnis negativ wurde. In diesem Fall lautet die Antwort: 1 mal, denn 123 - 100 macht 23. Versucht man erneut 100 anzuziehen, so ergibt sich eine negative Zahl.&lt;br /&gt;
Also muss eine &#039;1&#039; ausgeben werden. Die verbleibenden 23 werden weiterbehandelt, indem festgestellt wird wieviele Zehner darin enthalten sind. Auch hier wiederrum: In einer Schleife solange 10 abziehen, bis das Ergebnis nagativ wurde. Konkret geht das 2 mal gut, also muss das nächste auszugebende Zeichen ein &#039;2&#039; sein. Damit verbleiben noch die Einer, welche direkt in das entsprechende Zeichen umgewandelt werden können. In Summe hat man also an das Display die Zeichen &#039;1&#039; &#039;2&#039; &#039;3&#039; ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number:&lt;br /&gt;
           push  temp2            ; die Funktion verändert temp2, also sichern&lt;br /&gt;
                                  ; wir den Inhalt, um ihn am Ende wieder&lt;br /&gt;
                                  ; herstellen zu können&lt;br /&gt;
&lt;br /&gt;
           mov   temp2, temp1     ; das Register temp1 frei machen&lt;br /&gt;
                                  ; abzählen wieviele Hunderter&lt;br /&gt;
                                  ; in der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
           subi  temp2, 100       ; 100 abziehen&lt;br /&gt;
           brcs  lcd_number_2     ; ist dadurch ein Unterlauf entstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Hunderter mehr ...&lt;br /&gt;
           rjmp  lcd_number_1     ; ... und ab zur nächsten Runde&lt;br /&gt;
;&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorherhgehende Schleife 100 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; abzählen wieviele Zehner in&lt;br /&gt;
                  ; der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_3:&lt;br /&gt;
           subi  temp2, 10        ; 10 abziehen&lt;br /&gt;
           brcs  lcd_number_4     ; ist dadurch ein Unterlauf enstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Zehner mehr ...&lt;br /&gt;
           rjmp  lcd_number_3     ; ... und ab zur nächsten Runde&lt;br /&gt;
&lt;br /&gt;
                              ; die Zehnerstelle ausgeben&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; die übrig gebliebenen Einer&lt;br /&gt;
                  ; noch ausgeben&lt;br /&gt;
           ldi   temp1, &#039;0&#039;       ; die Zahl in temp2 ist jetzt im Bereich&lt;br /&gt;
           add   temp1, temp2     ; 0 bis 9. Einfach nur den ASCII Code für&lt;br /&gt;
           rcall lcd_data         ; &#039;0&#039; dazu addieren und wir erhalten dierekt&lt;br /&gt;
                                  ; den ASCII Code für die Ziffer&lt;br /&gt;
&lt;br /&gt;
           pop   temp2            ; den gesicherten Inhalt von temp2 wieder herstellen&lt;br /&gt;
           ret                    ; und zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beachte: Diese Funktion benutzt wiederrum die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039;. Anders als bei den bisherigen Aufrufen ist &#039;&#039;&#039;lcd_number&#039;&#039;&#039; aber darauf angewiesen, dass &#039;&#039;&#039;lcd_data&#039;&#039;&#039; das Register &#039;&#039;&#039;temp2&#039;&#039;&#039; unangetastet lässt. Falls sie es noch nicht getan haben, dann ist das jetzt die perfekte Gelegenheit, &#039;&#039;&#039;lcd_data&#039;&#039;&#039; mit den entsprechenden &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; Befehlen zu versehen. Sie sollten dies unbedingt zur Übung selbst machen. Am Ende muß die Funktion dann wie diese hier aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1           ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap  temp1                  ; Vertauschen&lt;br /&gt;
           andi  temp1, 0b00001111      ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr   temp1, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp1        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi  temp2, 0b00001111      ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr   temp2, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp2        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur ohne RS zu setzen&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           andi  temp1, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi  temp2, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz zur Funktionsweise der Funktion &#039;&#039;&#039;lcd_number&#039;&#039;&#039;: Die Zahl in einem Register bewegt sich im Wertebereich 0 bis 255. Um herauszufinden, wie die Hunderterstelle lautet, zieht die Funktion einfach in einer Schleife immer wieder 100 von der Schleife ab, bis bei der Subtraktion ein Unterlauf, angezeigt durch das Setzen des Carry-Bits bei der Subtraktion, entsteht. Die Anzahl wird im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; mitgezählt. Da dieses Register mit dem ASCII Code von &#039;0&#039; initialisiert wurde, und dieser ASCII Code bei jedem Schleifendurchlauf um 1 erhöht wird, können wir das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; direkt zur Ausgabe des Zeichens für die Hunderterstelle durch die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; benutzen. Völlig analog funktioniert auch die Ausgabe der Zehnerstelle.&lt;br /&gt;
&lt;br /&gt;
===Unterdrückung von führenden Nullen===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung: Diese Routine ist fehlerhaft&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt jede Zahl im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; immer mit 3 Stellen aus. Führende Nullen werden nicht unterdrückt. Möchte man dies ändern, so ist das ganz leicht möglich: Vor Ausgabe der Hunderterstelle bzw. Zehnerstelle muss lediglich überprüft werden, ob die Entsprechende Ausgabe eine &#039;0&#039; wäre. Ist sie das, so wird die Ausgabe übersprungen. Lediglich in der Einerstelle wird jede Ziffer wie errechnet ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ...&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben, wenn&lt;br /&gt;
                                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_2a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_2a:&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&lt;br /&gt;
           ...&lt;br /&gt;
                              ; die Zehnerstelle ausgeben, wenn&lt;br /&gt;
                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_4a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_4a:&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Verfahren, die einzelnen Stellen durch Subtraktion zu bestimmen, ist bei kleinen Zahlen eine durchaus gängige Alternative. Vor allem dann, wenn keine hardwaremäßige Unterstützung für Multiplikation und Division zur Verfügung steht. Ansonsten könnte man die die einzelnen Ziffern auch durch Division bestimmen. Das Prinzip ist folgendes (beispielhaft an der Zahl 52783 gezeigt)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   52783 / 10          -&amp;gt; 5278&lt;br /&gt;
   52783 - 5278 * 10   -&amp;gt;          3&lt;br /&gt;
&lt;br /&gt;
   5278 / 10           -&amp;gt; 527&lt;br /&gt;
   5278 - 527 * 10     -&amp;gt;          8&lt;br /&gt;
&lt;br /&gt;
   527 / 10            -&amp;gt; 52&lt;br /&gt;
   527 - 52 * 10       -&amp;gt;          7&lt;br /&gt;
&lt;br /&gt;
   52 / 10             -&amp;gt; 5&lt;br /&gt;
   52 - 5 * 10         -&amp;gt;          2&lt;br /&gt;
&lt;br /&gt;
   5 / 10              -&amp;gt; 0&lt;br /&gt;
   5 - 0 * 10          -&amp;gt;          5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Prinzip ist also die Restbildung bei einer fortgesetzten Division durch 10, wobei die einzelnen Ziffern in umgekehrter Reihenfolge ihrer Wertigkeit entstehen. Dadurch hat man aber ein Problem: Damit die Zeichen in der richtigen Reihenfolge ausgegeben werden können, muß man sie meistens zwischenspeichern um sie in der richtigen Reihenfole ausgeben zu können. Wird die Zahl in einem Feld von immer gleicher Größe ausgegeben, dann kann man auch die Zahl von rechts nach links ausgeben (bei einem LCD ist das möglich).&lt;br /&gt;
&lt;br /&gt;
===Hexadezimal ausgeben===&lt;br /&gt;
&lt;br /&gt;
Zu guter letzt hier noch eine Funktion, die eine Zahl aus dem Register &#039;&#039;&#039;temp1&#039;&#039;&#039; in hexadezimaler Form ausgibt. Die Funktion weist keine Besonderheiten auf und sollte unmittelbar verständlich sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen hexadezimal ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number_hex:&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           rcall lcd_number_hex_digit&lt;br /&gt;
           swap  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_number_hex_digit:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
           andi  temp1, $0F&lt;br /&gt;
           cpi   temp1, 10&lt;br /&gt;
           brlt  lcd_number_hex_digit_1&lt;br /&gt;
           subi  temp1, -( &#039;A&#039; - &#039;9&#039; - 1 ) ; es wird subi mit negativer Konstante verwendet, weil es kein addi gibt&lt;br /&gt;
lcd_number_hex_digit_1:&lt;br /&gt;
           subi  temp1, -&#039;0&#039;               ; ditto&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           &lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Eine 16-Bit Zahl aus einem Registerpärchen ausgeben===&lt;br /&gt;
&lt;br /&gt;
Um eine 16 Bit Zahl auszugeben wird wieder das bewährte Schema benutzt die einzelnen Stellen durch Subtraktion abzuzählen. Da es sich hierbei allerdings um eine 16 Bit Zahl handelt, müssen die Subtraktionen als 16-Bit Arithmetik ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 16 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp2 (low Byte) / temp3 (high Byte)&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number16:&lt;br /&gt;
           push  temp1&lt;br /&gt;
           push  temp2&lt;br /&gt;
           push  temp3&lt;br /&gt;
&lt;br /&gt;
; die Zehntausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number0:&lt;br /&gt;
           subi  temp2, low(10000)&lt;br /&gt;
           sbci  temp3, high(10000)&lt;br /&gt;
           brcs  lcd_number1&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number0&lt;br /&gt;
&lt;br /&gt;
; .. und ausgeben&lt;br /&gt;
lcd_number1:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-10000)&lt;br /&gt;
           sbci  temp3, high(-10000)&lt;br /&gt;
&lt;br /&gt;
; die Tausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number2:&lt;br /&gt;
           subi  temp2, low(1000)&lt;br /&gt;
           sbci  temp3, high(1000)&lt;br /&gt;
           brcs  lcd_number3&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number2&lt;br /&gt;
&lt;br /&gt;
; ... und ausgeben&lt;br /&gt;
lcd_number3:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-1000)&lt;br /&gt;
           sbci  temp3, high(-1000)&lt;br /&gt;
&lt;br /&gt;
; Als nächtes kommt die Hunderterstelle drann&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number4:&lt;br /&gt;
           subi  temp2, low(100)&lt;br /&gt;
           sbci  temp3, high(100)&lt;br /&gt;
           brcs  lcd_number5&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number4&lt;br /&gt;
&lt;br /&gt;
; und ausgeben&lt;br /&gt;
lcd_number5:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100&lt;br /&gt;
&lt;br /&gt;
; bleiben noch die Zehner&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number6:&lt;br /&gt;
           subi  temp2, 10&lt;br /&gt;
           brcs  lcd_number7&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number6&lt;br /&gt;
&lt;br /&gt;
; ausgeben ...&lt;br /&gt;
lcd_number7:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10&lt;br /&gt;
&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
           add   temp1, temp2&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
; fertig. Stack wieder aufräumen&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           pop   temp3&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Der überarbeitete, komplette Code==&lt;br /&gt;
&lt;br /&gt;
Hier also die komplett überarbeitete Version der LCD Funktionen.&lt;br /&gt;
&lt;br /&gt;
Die für die Benutzung relevanten Funktionen&lt;br /&gt;
* &#039;&#039;&#039;lcd_init&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_clear&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_home&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_data&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_flash_string&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number_hex&#039;&#039;&#039;&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; übergeben, wobei &#039;&#039;&#039;temp1&#039;&#039;&#039; vom Usercode definiert werden muss.&lt;br /&gt;
&lt;br /&gt;
[[Media:lcd-routines.asm|Download lcd-routines.asm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Stack|&lt;br /&gt;
zurücklink=AVR-Tutorial: Stack|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Interrupts|&lt;br /&gt;
vorlink=AVR-Tutorial: Interrupts}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister SREG) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register temp1 übergeben, wobei temp1 vom Usercode definiert werden muss. &lt;br /&gt;
&lt;br /&gt;
der progger hat vergessen das Z reg in den sub&#039;s lcd_flash_string ...&lt;br /&gt;
zu sichern, aber danke für den Einstig&lt;br /&gt;
&lt;br /&gt;
Lars&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27998</id>
		<title>AVR-Tutorial: LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27998"/>
		<updated>2008-05-16T03:13:58Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* ASCII */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kaum ein elektronisches Gerät kommt heutzutage noch ohne ein LCD daher. Ist doch auch praktisch, Informationen im Klartext anzeigen zu können, ohne irgendwelche LEDs blinken zu lassen. Kein Wunder also, dass die häufigste Frage in Mikrocontroller-Foren ist: &amp;quot;Wie kann ich ein LCD anschließen?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-Bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-Bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-Bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-Bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-Bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-Bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4 Bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert. Ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitet hat. Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt, da wir wissen, welche Anschlüsse das LCD benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Pinnummer&amp;lt;BR&amp;gt;LCD || Bezeichnung || Anschluss&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |1 || Vss || GND&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |2 || Vcc || 5V&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |3 || Vee || GND oder [[Potentiometer | Poti]] &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |4 || RS || PD4 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |5 || RW || GND &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |6 || E || PD5 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |7 || DB0 || nicht angeschlossen &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |8 || DB1 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |9 || DB2 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |10 || DB3 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |11 || DB4 || PD0 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |12 || DB5 || PD1 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |13 || DB6 || PD2 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |14 || DB7 || PD3 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |15 || A || Vorsicht! Meistens nicht direkt an +5V anschließbar,&amp;lt;BR&amp;gt;nur über einen Vorwiderstand (z.B. 33&amp;amp;Omega;)!&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |16 || K || GND&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Ok alles ist verbunden. Wenn man jetzt den Strom einschaltet, sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display?&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung des LCDs im 4-Bit-Modus ==&lt;br /&gt;
&lt;br /&gt;
Um ein Byte zu übertragen, muss man es erstmal in die beiden Nibbles zerlegen, die getrennt übertragen werden. Da das obere Nibble (Bit 4 - Bit 7) als erstes übertragen wird, die 4 Datenleitungen jedoch an die vier unteren Bits des Port D angeschlossen sind, muss man die beiden Nibbles des zu übertragenden Bytes erstmal vertauschen. Der AVR kennt dazu praktischerweise einen eigenen Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           swap r16               ; vertauscht die beiden Nibbles von r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus 0b00100101 wird so z.B. 0b01010010. &lt;br /&gt;
&lt;br /&gt;
Jetzt sind die Bits für die erste Phase der Übertragung an der richtigen Stelle. Trotzdem wollen wir das Ergebnis nicht einfach so mit &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; an den Port geben. Um die Hälfte des Bytes, die jetzt nicht an die Datenleitungen des LCDs gegeben wird auf null zu setzen, verwendet man folgenden Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           andi r16, 0b00001111   ; Nur die vier unteren (mit 1 markierten)&lt;br /&gt;
                                  ; Bits werden übernommen, alle anderen werden null&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also: Das obere Nibble wird erst mit dem unteren vertauscht, damit es unten ist. Dann wird das obere (das wir jetzt noch nicht brauchen) auf null gesetzt. &lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem LCD noch mitteilen, ob wir Daten oder Befehle senden wollen. Das machen wir, indem wir das Bit, an dem RS angeschlossen ist (PD4), auf 0 (Befehl senden) oder auf 1 (Daten senden) setzen. Um ein Bit in einem normalen Register zu setzen, gibt es den Befehl sbr (Set Bit in Register). Dieser Befehl unterscheidet sich jedoch von sbi (das nur für IO-Register gilt) dadurch, dass man nicht die Nummer des zu setzenden Bits angibt, sondern eine Bitmaske. Das geht so: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbr r16, 0b00010000     ; Bit 4 setzen, alle anderen Bits bleiben gleich&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RS ist an PD4 angeschlossen. Wenn wir r16 an den Port D ausgeben, ist RS jetzt also high und das LCD erwartet Daten anstatt von Befehlen. &lt;br /&gt;
&lt;br /&gt;
Das Ergebnis können wir jetzt endlich direkt an den Port D übergeben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           out PORTD, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss vorher der Port D auf Ausgang geschalten werden, indem man 0xFF ins Datenrichtungsregister DDRD schreibt. &lt;br /&gt;
&lt;br /&gt;
Um dem LCD zu signalisieren, dass es das an den Datenleitungen anliegende Nibble übernehmen kann, wird die E-Leitung (Enable, an PD5 angeschlossen) auf high und kurz darauf wieder auf low gesetzt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbi PORTD, 5              ; Enable high&lt;br /&gt;
           nop                       ; 3 Taktzyklen warten (&amp;quot;nop&amp;quot; = nichts tun)&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5              ; Enable wieder low&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die eine Hälfte des Bytes wäre damit geschafft! Die andere Hälfte kommt direkt hinterher: Alles, was an der obenstehenden Vorgehensweise geändert werden muss, ist, das &amp;quot;swap&amp;quot; (Vertauschen der beiden Nibbles) wegzulassen.&lt;br /&gt;
&lt;br /&gt;
== Initialisierung des Displays ==&lt;br /&gt;
&lt;br /&gt;
Allerdings gibt es noch ein Problem. Wenn ein LCD eingeschaltet wird, dann läuft es zunächst im 8 Bit Modus. Irgendwie muss das Display initialisiert und auf den 4 Bit Modus umgeschaltet werden, und zwar nur mit den 4 zur Verfügung stehenden Datenleitungen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn es Probleme gibt, dann meistens an diesem Punkt. Die &amp;quot;kompatiblen&amp;quot; Kontroller sind gelegentlich doch nicht 100% identisch. Es lohnt sich, das Datenblatt (siehe Weblinks im Artikel [[LCD]]) genau zu lesen, in welcher Reihenfolge und mit welchen Abständen (Delays) die Initialisierungbefehle gesendet werden. Eine weitere Hilfe können Ansteuerungsbeispiele in Forenbeiträgen geben z.B.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79609#664268 (A) KS0066U oder Ähnliche --- LCD Treiber]&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 4 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Achtung: Im Folgenden sind alle Bytes aus Sicht des LCD-Kontrollers angegeben! Da LCD-seitig nur die Leitungen DB4 - DB7 verwendet werden, ist daher immer nur das höherwertige Nibble gültig. Durch die Art der Verschaltung (DB4 - DB7 wurde auf dem PORT an PD0 bis PD3 angeschlossen) ergibt sich eine Verschiebung, so dass das am Kontroller auszugebende Byte nibblemässig vertauscht ist!&lt;br /&gt;
&lt;br /&gt;
Die Sequenz, aus Sicht des Kontrollers, sieht so aus:&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* $2 ins Steuerregister schreiben (RS = 0), dadurch wird auf 4 Bit Daten umstellt&lt;br /&gt;
* Ab jetzt muss für die Übertragung eines Bytes jeweils zuerst das höherwertige Nibble und dann das niederwertige Nibble übertragen werden, wie oben beschrieben&lt;br /&gt;
* Mit dem Konfigurier-Befehl $20 das Display konfigurieren (4-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 8 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Der Vollständigkeit halber hier noch die notwendige Initialiserungssequenz für den 8 Bit Modus. Da hier die Daten komplett als 1 Byte übertragen werden können, sind einige Klimmzüge wie im 4 Bit Modus nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mit dem Konfigurier-Befehl 0x30 das Display konfigurieren (8-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
== Routinen zur LCD-Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
Die Routinen zur Kommunikation mit dem LCD sehen also so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000 (Anm.1)&lt;br /&gt;
           out PORTD, temp1             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000&lt;br /&gt;
           out PORTD, temp2             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out PORTD, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
 ;&lt;br /&gt;
 ; Bei höherem Takt (&amp;gt;= 8 MHz) kann es notwendig sein, &lt;br /&gt;
 ; vor dem Enable High 1-2 Wartetakte (nop) einzufügen. &lt;br /&gt;
 ; Siehe dazu http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi PORTD, 5                 ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5                 ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
&lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi  temp3,50&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall  delay5ms&lt;br /&gt;
           dec  temp3&lt;br /&gt;
           brne powerupwait&lt;br /&gt;
           ldi temp1, 0b00000011        ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out PORTD, temp1             ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00000010        ; 4bit-Modus einstellen&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00101000        ; 4Bit / 2 Zeilen / 5x8&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00001100        ; Display ein / Cursor aus / kein Blinken&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00000100        ; inkrement / kein Scrollen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi temp1, 0b00000001   ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi temp1, 0b00000010   ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anm.1: Siehe [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
Weitere Funktionen (wie z.B. Cursorposition verändern) sollten mit Hilfe der [[AVR-Tutorial:_LCD#Welche_Befehle_versteht_das_LCD.3F|Befehlscodeliste]] nicht schwer zu realisieren sein. Einfach den Code in temp laden, lcd_command aufrufen und ggf. eine Pause einfügen. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man die LCD-Ansteuerung auch an einen anderen Port des Mikrocontrollers &amp;quot;verschieben&amp;quot;: Wenn das LCD z.B. an Port B angeschlossen ist, dann reicht es, im Programm alle &amp;quot;PORTD&amp;quot; durch &amp;quot;PORTB&amp;quot; und &amp;quot;DDRD&amp;quot; durch &amp;quot;DDRB&amp;quot; zu ersetzen. &lt;br /&gt;
&lt;br /&gt;
Wer eine höhere Taktfrequenz als 4 MHz verwendet, der sollte daran denken, die Dauer der Verzögerungsschleifen anzupassen.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das diese Routinen zur Anzeige von Text verwendet, kann z.B. so aussehen (die Datei lcd-routines.asm muss sich im gleichen Verzeichnis befinden). Nach der Initialisierung wird zuerst der Displayinhalt gelöscht. Um dem LCD ein Zeichen zu schicken, lädt man es in temp1 und ruft die Routine &amp;quot;lcd_data&amp;quot; auf. Das folgende Beispiel zeigt das Wort &amp;quot;Test&amp;quot; auf dem LCD an. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/lcd-test.asm Download lcd-test.asm] &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;
&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, 0xFF    ; Port D = Ausgang&lt;br /&gt;
           out DDRD, temp1&lt;br /&gt;
&lt;br /&gt;
           rcall lcd_init     ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear    ; Display löschen&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;T&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;e&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           &lt;br /&gt;
           ldi temp1, &#039;s&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;t&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD-Routinen werden hier eingefügt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für längere Texte ist die Methode, jedes Zeichen einzeln in das Register zu laden und &amp;quot;lcd_data&amp;quot; aufzurufen natürlich nicht sehr praktisch. Dazu später aber mehr.&lt;br /&gt;
&lt;br /&gt;
Bisher wurden in Register immer irgendwelche Zahlenwerte geladen, aber in diesem Programm kommt plötzlich die Anweisung&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
vor. Wie ist diese zu verstehen? Passiert hier etwas grundlegend anderes als beim Laden einer Zahl in ein Register?&lt;br /&gt;
&lt;br /&gt;
Die Antwort darauf lautet: Nein. Auch hier wird letztendlich nur eine Zahl in ein Register geladen. Der Schlüssel zum Verständnis beruht darauf, dass zum LCD, so wie zu allen Ausgabegeräten, für die Ausgabe von Texten immer nur Zahlen übertragen werden, sog. Codes. Zum Beispiel könnte man vereinbaren, dass ein LCD, wenn es den Ausgabecode 65 erhält, ein &#039;A&#039; anzeigt, bei einem Ausgabecode von 66 ein &#039;B&#039; usw. Naturgemäß gibt es daher viele verschiedene Code-Buchstaben Zuordnungen. Damit hier etwas Ordnung in das potentielle Chaos kommt, hat man sich bereits in der Steinzeit der Programmierung auf bestimmte Codetabellen geeinigt, von denen die verbreitetste sicherlich die ASCII-Zuordnung ist.&lt;br /&gt;
&lt;br /&gt;
==ASCII==&lt;br /&gt;
&lt;br /&gt;
ASCII steht für &#039;&#039;American Standard Code for Information Interchange&#039;&#039; und ist ein standardisierter Code zur Zeichenumsetzung. Die Codetabelle sieht hexadezimal dabei wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;.0&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x1&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x2&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x3&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x4&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x6&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x7&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x8&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x9&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xA&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xB&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xC&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xD&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xE&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xF&amp;lt;/th&amp;gt;&amp;lt;br&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;&#039;&#039;&#039;0x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NUL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SOH&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;STX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EOT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ENQ&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ACK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BEL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;HT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;VT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CR&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SO&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SI&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;&#039;&#039;&#039;1x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DLE&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NAK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SYN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CAN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EM&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SUB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ESC&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;US&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;&#039;&#039;&#039;2x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SP&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;!&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;#&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;%&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;amp;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;(&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;)&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;*&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;+&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;,&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;.&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;3x&#039;&#039;&#039;&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;&amp;lt;td&amp;gt;3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;9&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;=&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;gt;&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;4x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;B&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;C&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;D&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;F&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;G&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;H&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;I&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;J&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;L&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;M&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;N&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;O&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;&#039;&#039;&#039;5x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;P&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;R&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;S&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;T&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;U&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;V&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;W&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;X&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;[&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;\&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;]&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;^&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;6x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;`&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;a&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;b&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;c&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;d&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;e&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;f&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;g&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;h&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;i&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;j&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;k&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;l&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;m&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;n&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;o&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;&#039;&#039;&#039;7x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;p&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;r&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;s&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;t&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;u&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;v&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;w&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;{&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;|&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;~&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DEL&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 ersten beiden Zeilen enthalten die Codes für einige Steuerzeichen, ihre vollständige Beschreibung würde hier zu weit führen. Das Zeichen &#039;&#039;&#039;SP&#039;&#039;&#039; steht für ein &#039;&#039;Space&#039;&#039;, also ein Leerzeichen. &#039;&#039;&#039;BS&#039;&#039;&#039; steht für &#039;&#039;Backspace&#039;&#039;, also ein Zeichen zurück. &#039;&#039;&#039;DEL&#039;&#039;&#039; steht für &#039;&#039;Delete&#039;&#039;, also das Löschen eines Zeichens. &#039;&#039;&#039;CR&#039;&#039;&#039; steht für &#039;&#039;Carriage Return&#039;&#039;, also wörtlich: der Wagenrücklauf (einer Schreibmaschine), während &#039;&#039;&#039;LF&#039;&#039;&#039; für &#039;&#039;Line feed&#039;&#039;, also einen Zeilenvorschub steht.&lt;br /&gt;
&lt;br /&gt;
Der Assembler kennt diese Codetabelle und ersetzt die Zeile&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, $54&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
was letztendlich auch der Lesbarkeit des Programmes zugute kommt. Funktional besteht kein Unterschied zwischen den beiden Anweisungen. Beide bewirken, dass das Register temp1 mit dem Bitmuster 01010100 ( = hexadezimal 54, = dezimal 84 oder eben der ASCII Code für &#039;&#039;&#039;T&#039;&#039;&#039;) geladen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das LCD wiederrum kennt diese Code-Tabelle ebenfalls und wenn es über den Datenbus die Codezahl $54 zur Anzeige empfängt, dann schreibt es ein &#039;&#039;&#039;T&#039;&#039;&#039; an die aktuelle Cursorposition. Genauer gesagt, weiss das LCD nichts von einem &#039;&#039;&#039;T&#039;&#039;&#039;. Es sieht einfach in seinen internen Tabellen nach, welche Pixel beim Empfang der Codezahl $54 auf schwarz zu setzen sind. &#039;Zufällig&#039; sind das genau jene Pixel, die für uns Menschen ein &#039;&#039;&#039;T&#039;&#039;&#039; ergeben.&lt;br /&gt;
&lt;br /&gt;
==Welche Befehle versteht das LCD?==&lt;br /&gt;
&lt;br /&gt;
Auf dem LCD arbeitet ein Kontroller vom Typ HD44780. Diesen Kontroller versteht eine Reihe von Befehlen, die allesamt mittels lcd_command gesendet werden können. Ein Kommando ist dabei nichts anderes als ein Befehlsbyte, indem die verschiedenen Bits verschiedene Bedeutung besitzen:&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 0 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 1 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;der Zustand dieses Bits ist egal&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;sonstige Buchstaben&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;das Bit muss je nach gewünschter Funktionalität gesetzt werden. Die mögliche Funktionalität des jeweiligen Bits geht aus der Befehlsbeschreibung hervor&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Kommando &#039;ON/OFF Control&#039; soll benutzt werden um das Display einzuschalten, der Cursor soll eingeschaltet werden und der Cursor soll blinken.&lt;br /&gt;
Das Befehlsbyte ist so aufgebaut:&lt;br /&gt;
   0b00001dcb&lt;br /&gt;
Aus der Befehlsbeschreibung entnimmt man:&lt;br /&gt;
* Display ein bedeutet, dass an der Bitposition d eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor ein bedeutet, dass an der Bitposition c ein 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor blinken bedeutet, dass an der Bitposition b eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
Das dafür zu übertragende Befehlsbyte hat also die Gestalt 0b00001111 oder in hexadezimaler Schreibweise $0F&lt;br /&gt;
&lt;br /&gt;
===Clear display: 0b00000001===&lt;br /&gt;
&lt;br /&gt;
Die Anzeige wird gelöscht und der Ausgabecursor kehrt an die Home Position (links, erste Zeile) zurück&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Cursor home: 0b0000001x===&lt;br /&gt;
&lt;br /&gt;
Der Cursor kehrt an die Home Position (links, erste Zeile) zurück. Ein verschobenes Display wird auf die Grundeinstellung zurückgesetzt.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs bis 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Entry mode: 0b000001is===&lt;br /&gt;
&lt;br /&gt;
Legt die Cursor Richtung fest sowie eine mögliche Verschiebung des Displays fest&lt;br /&gt;
* i = 1, Cursorposition bei Ausgabe eines Zeichens erhöhen&lt;br /&gt;
* i = 0, Cursorposition bei Ausgabe eines Zeichens vermindern&lt;br /&gt;
* s = 1, Display wird gescrollt, wenn der Cursor das Ende/Anfang, je nach Einstellung von i, erreicht hat.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===On/off control: 0b00001dcb===&lt;br /&gt;
&lt;br /&gt;
Display insgesamt ein/ausschalten; den Cursor ein/ausschalten; den Cursor auf blinken schalten/blinken aus. Wenn das Display ausgeschaltet wird, geht der Inhalt des Displays nicht verloren. Der vorher angezeigte Text wird nach wiedereinschalten erneut angezeigt.&lt;br /&gt;
Ist der Cursor eingeschaltet, aber Blinken ausgeschaltet, so wird der Cursor als Cursorzeile in Pixelzeile 8 dargestellt. Ist Blinken eingeschaltet, wird der Cursor als blinkendes ausgefülltes Rechteck dargestellt, welches abwechselnd mit dem Buchstaben an dieser Stelle angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
* d = 0, Display aus&lt;br /&gt;
* d = 1, Display ein&lt;br /&gt;
* c = 0, Cursor aus&lt;br /&gt;
* c = 1, Cursor ein&lt;br /&gt;
* b = 0, Cursor blinken aus&lt;br /&gt;
* b = 1, Cursor blinken ein&lt;br /&gt;
 &lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Cursor/Scrollen: 0b0001srxx===&lt;br /&gt;
&lt;br /&gt;
Bewegt den Cursor oder scrollt das Display um eine Position entweder nach rechts oder nach links.&lt;br /&gt;
&lt;br /&gt;
* s = 1, Display scrollen&lt;br /&gt;
* s = 0, Cursor bewegen&lt;br /&gt;
* r = 1, nach rechts&lt;br /&gt;
* r = 0, nach links &lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Konfiguration: 0b001dnfxx===&lt;br /&gt;
&lt;br /&gt;
Einstellen der Interface Art, Modus, Font&lt;br /&gt;
* d = 0, 4-Bit Interface&lt;br /&gt;
* d = 1, 8-Bit Interface&lt;br /&gt;
* n = 0, 1 zeilig&lt;br /&gt;
* n = 1, 2 zeilig&lt;br /&gt;
* f = 0, 5x7 Pixel&lt;br /&gt;
* f = 1, 5x11 Pixel&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Character RAM Address Set: 0b01aaaaaa===&lt;br /&gt;
&lt;br /&gt;
Mit diesem Kommando werden maximal 8 selbst definierte Zeichen definiert. Dazu wird der Character RAM Zeiger auf den Anfang des Character Generator (CG) RAM gesetzt und das Zeichen durch die Ausgabe von 8 Byte definiert. Der Adress Zeiger wird nach Ausgabe jeder Pixelzeile (8Bit) vom LCD selbst erhöht. Nach Beendigung der Zeichendefinition muss die Schreibposition explizit mit dem Kommando &amp;quot;Display RAM Address Set&amp;quot; wieder in den DD-RAM Bereich gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
aaaaaa 6-bit CG RAM Adresse&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Display RAM Address Set: 0b1aaaaaaa===&lt;br /&gt;
&lt;br /&gt;
Den Cursor neu positionieren. Display Data (DD) Ram ist vom Character Generator (CG) Ram unabhängig. Der Adresszeiger wird bei Ausgabe eines Zeichens ins DD Ram automatisch erhöht. Das Display verhält sich so, als ob eine Zeile immer aus 32 logischen Zeichen besteht, von der, je nach konkretem Displaytyp (16 Zeichen, 20 Zeichen) immer nur ein Teil sichtbar ist.&lt;br /&gt;
&lt;br /&gt;
aaaaaaa 7-bit DD RAM Adresse. Auf 2-zeiligen Displays (und den meisten 16x1 Displays), kann die Adressangabe wie folgt interpretiert werden&lt;br /&gt;
&lt;br /&gt;
1laaaaaa&lt;br /&gt;
* l = Zeilennummer (0 oder 1)&lt;br /&gt;
* a = 6-Bit Spaltennummer&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
==Einschub: Code aufräumen==&lt;br /&gt;
&lt;br /&gt;
Es wird Zeit sich einmal etwas kritisch mit den bisher geschriebenen Funktionen auseinander zu setzen.&lt;br /&gt;
&lt;br /&gt;
===Portnamen aus dem Code herausziehen===&lt;br /&gt;
&lt;br /&gt;
Wenn wir die LCD-Funktionen einmal genauer betrachten, dann fällt sofort auf, daß über die Funktionen verstreut immer wieder das &#039;&#039;&#039;PORTD&#039;&#039;&#039; sowie einzelne Zahlen für die Pins an diesem Port auftauchen. Wenn das LCD an einem anderen Port betrieben werden soll, oder sich die Pin-Belegung ändert, dann muß an all diesen Stellen eine Anpassung vorgenommen werden. Dabei darf keine einzige Stelle übersehen werden, ansonsten würden die LCD-Funktionen nicht oder nicht vollständig funktionieren.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit dem vorzubeugen ist es, diese immer gleichbleibenden Dinge an den Anfang der LCD-Funktionen vorzuziehen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
.equ PIN_E    = 5&lt;br /&gt;
.equ PIN_RS   = 4&lt;br /&gt;
&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp1          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp2          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi LCD_PORT, PIN_E          ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi LCD_PORT, PIN_E          ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
 &lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf Ausgang&lt;br /&gt;
           out   LCD_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi   temp3,6&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           dec   temp3&lt;br /&gt;
           brne  powerupwait&lt;br /&gt;
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out   LCD_PORT, temp1        ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00101000      ; 4 Bot, 2 Zeilen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00001100      ; Display on, Cursor off&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00000100      ; endlich fertig&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi   temp1, 0b00000010      ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels &#039;&#039;&#039;.equ&#039;&#039;&#039; werden mit dem Assembler Textersetzungen vereinbart. Der Assembler ersetzt alle Vorkomnisse des Quelltextes durch den zu ersetzenden Text. Dadurch ist es zb. möglich alle Vorkommnisse von &#039;&#039;&#039;PORTD&#039;&#039;&#039; durch &#039;&#039;&#039;LCD_PORT&#039;&#039;&#039; auszutauschen. Wird das LCD an einen anderen Port, zb. &#039;&#039;&#039;PORTB&#039;&#039;&#039; gelegt, dann genügt es, einzig und alleine die Zeilen&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTB&lt;br /&gt;
.equ LCD_DDR  = DDRB&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu ersetzen. Der Assembler sorgt dann dafür, dass diese Portänderung an den relevanten Stellen im Code über die Textersetzungen einfliesst. Selbiges natürlich mit der Pin-Zuordnung.&lt;br /&gt;
&lt;br /&gt;
===Registerbenutzung===&lt;br /&gt;
&lt;br /&gt;
Bei diesen Funktionen mussten einige Register des Prozessors benutzt werden um darin Zwischenergebnisse zu speichern bzw. zu bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss dabei natürlich, dass es zu keinen Überschneidungen kommt. Solange nur jede Funktion jeweils für sich betrachtet wird, ist das kein Problem. In 20 oder 30 Code-Zeilen kann man gut verfolgen, welches Register wofür benutzt wird. Schwieriger wird es, wenn Funktionen wiederrum andere Funktionen aufrufen, die ihrerseits wieder Funktionen aufrufen usw. Jede dieser Funktionen benutzt einige Register und mit zunehmender Programmgröße wird es immer schwieriger zu verfolgen, welches Register zu welchem Zeitpunkt wofür benutzt wird.&lt;br /&gt;
&lt;br /&gt;
Speziell bei Basisfunktionen, wie diesen LCD-Funktionen, ist es daher oft ratsam, dafür zu sorgen, daß jede Funktion die Register wieder in dem Zustand hinterlässt, indem sie sie auch vorgefunden hat. Wir benötigen dazu wieder den Stack, auf dem die Registerinhalte bei Betreten einer Funktion zwischengespeichert werden und von dem die Register bei Verlassen einer Funktion wiederhergestellt werden.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir die Funktion&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion verändert das Register temp1. Um das Register abzusichern schreiben wir die Funktion um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           push  temp1                  ; temp1 auf dem Stack sichern&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           pop   temp1                  ; temp1 vom Stack wiederherstellen&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am besten hält man sich an die Regel: Jede Funktion ist dafür zuständig die Register zu sichern und wiederherzustellen, die sie auch selbst verändert. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; ruft die Funktionen &#039;&#039;&#039;lcd_command&#039;&#039;&#039; und &#039;&#039;&#039;delay5ms&#039;&#039;&#039; auf. Wenn diese Funktionen selbst wieder Register verändern (und das tun sie), so ist es die Aufgabe dieser Funktionen, sich um die Sicherung und das Wiederherstellen der entsprechenden Register zu kümmern. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; sollte sich nicht darum kümmern müssen. Auf diese Weise ist das Schlimmste, das einem passieren kann, das ein paar Register unnütz gesichert und wiederhergestellt werden. Das kostet zwar etwas Rechenzeit und etwas Speicherplatz auf dem Stack, ist aber immer noch besser als das andere Extrem: Nach einem Funktionsaufruf haben einige Register nicht mehr den Wert den sie haben sollten und das Programm rechnet mit falschen Zahlen weiter.&lt;br /&gt;
&lt;br /&gt;
===Lass den Assembler rechnen===&lt;br /&gt;
Betrachtet man den Code genauer, so fallen einige konstante Zahlenwerte auf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Das vorangestellte $ kennzeichnet die Zahl als Hexadezimalzahl) Der Code benötigt eine Warteschleife die mindestens 50µs dauert. Die beiden Befehle innerhalb der Schleife benötigen 3 Takte: 1 Takt für den &#039;&#039;&#039;dec&#039;&#039;&#039; und der &#039;&#039;&#039;brne&#039;&#039;&#039; benötigt 2 Takte wenn die Bedingung zutrifft, der Branch also genommen wird. Bei 4 Mhz werden also 4000000 / 3 * 50 / 1000000 = 66.6 Durchläufe durch die Schleife benötigt um eine Verzögerungszeit von 50µs (0.000050 Sekunden) zu erreichen, hexadezimal ausgedrückt: $42&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist: Bei anderen Taktfrequenzen müsste man nun jedesmal diese Berechnung machen und den entsprechenden Zahlenwert einsetzen. Das kann aber der Assembler genausogut erledigen. Am Anfang des Codes wird ein Eintrag definiert, der die Taktfrequenz festlegt. Traditionell heist dieser Eintrag &amp;lt;i&amp;gt;XTAL&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ XTAL  = 4000000&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 50 / 3 ) / 1000000&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An einer anderen Codestelle gibt es weitere derartige magische Zahlen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was geht hier vor?&lt;br /&gt;
Die innere Schleife benötigt wieder 3 Takte pro Durchlauf. Bei $C9 = 201 Durchläufen werden also 201 * 3 = 603 Takte verbraucht. In der äußeren Schleife kommen pro Durchlauf alo 1 + 603 + 1 + 2 = 607 Takte verbraucht. Da die äußere Schleife $21 = 33 mal wiederholt wird, werden 20031 Takte verbraucht. Bei 4Mhz benötigt der Prozessor 20031 / 4000000 = 0.005007 Sekunden, also 5 ms.&lt;br /&gt;
Wird der Wiederholwert für die innere Schleife bei $C9 belassen, so werden 4000000 / 607 * 5 / 1000 Wiederholungen der äusseren Schleife benötigt. Auch diese Berechnung kann wieder der Assembler übernehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 5 / 607 ) / 1000&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein kleines Problem kann bei der Verwendung dieses Verfahrens entstehen: Bei hohen Taktfrequenzen und großen Wartezeiten kann der berechnete Wert größer als 255 werden und man bekommt die Fehlermeldung &amp;quot;Operand(s) out of range&amp;quot; beim Assemblieren. Dieser Fall tritt zum Beispiel für obige Konstruktion bei einer Taktfrequenz von 16 MHz ein (genauer gesagt ab 15,3 MHz), während darunter XTAL beliebig geändert werden kann. Als einfachste Lösung bietet es sich an, die Zahl der Takte pro Schleifendurchlauf durch das Einfügen von &#039;&#039;&#039;nop&#039;&#039;&#039; zu erhöhen und die Berechnungsvorschrift anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Ausgabe eines konstanten Textes ==&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde schon einmal ein Text ausgegeben. Dies geschah durch Ausgabe von einzelnen Zeichen. Das können wir auch anders machen. Wir können den Text im Speicher ablegen und eine Funktion schreiben, die die einzelnen Zeichen aus dem Speicher holt und ausgibt. Dabei erhebt sich aber eine Fragestellung: Woher weiß die Funktion eigentlich, wie lange der Text ist? Die Antwort darauf lautet: Sie kann es nicht wissen. Wir müssen irgendwelche Vereinbarungen treffen, woran die Funktion erkennen kann, dass der Text zu Ende ist. Im Wesentlichen werden dazu 2 Methoden benutzt:&lt;br /&gt;
* Der Text enthält ein spezielles Zeichen, welches das Ende des Textes markiert&lt;br /&gt;
* Wir speichern nicht nur den Text selbst, sondern auch die Länge des Textes&lt;br /&gt;
Mit einer der beiden Methoden ist es der Textausgabefunktion dann ein Leichtes, den Text vollständig auszugeben.&lt;br /&gt;
&lt;br /&gt;
Wir werden uns im Weiteren dafür entscheiden, ein spezielles Zeichen, eine 0, dafür zu benutzen. Die Ausgabefunktionen werden dann etwas einfacher, als wenn bei der Ausgabe die Anzahl der bereits ausgegebenen Zeichen mitgezählt werden muss.&lt;br /&gt;
&lt;br /&gt;
Den Text selbst speichern wir im Flash-Speicher, also dort wo auch das Programm gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Einen konstanten Text aus dem Flash Speicher&lt;br /&gt;
 ; ausgeben. Der Text wird mit einer 0 beendet&lt;br /&gt;
lcd_flash_string:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_1:&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           rjmp  lcd_flash_string_1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_2:&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion benutzt den Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039; um das jeweils nächste Zeichen aus dem Flash Speicher in ein Register zur Weiterverarbeitung zu laden. Dazu wird der sog. &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; benutzt. So nennt man das Registerpaar &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;. Nach jedem Ladevorgang wird dabei durch den Befehl&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
dieser Z-Pointer um 1 erhöht. Mittels &#039;&#039;&#039;cpi&#039;&#039;&#039; wird das in das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; geladene Zeichen mit 0 verglichen. &#039;&#039;&#039;cpi&#039;&#039;&#039; vergleicht die beiden Zahlen und merkt sich das Ergebnis in einem speziellen Register in Form von Status Bits. &#039;&#039;&#039;cpi&#039;&#039;&#039; zieht dabei ganz einfach die beiden Zahlen voneinander ab. Sind sie gleich, so kommt da als Ergebnis 0 heraus und &#039;&#039;&#039;cpi&#039;&#039;&#039; setzt daher konsequenter Weise das Zero-Flag, das anzeigt, daß die vorhergegangene Operation ein 0 Ergebnis hatte.&#039;&#039;&#039;breq&#039;&#039;&#039; wertet diese Status-Bits aus. Wenn die vorhergegangene Operation ein 0-Ergebnis hatte, das Zero-Flag also gesetzt ist, dann wird ein Sprung zum angegebenen Label durchgeführt. In Summe bewirkt also die Sequenz&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
das das gelesene Zeichen mit 0 verglichen wird und falls das gelesene&lt;br /&gt;
Zeichen tatsächlich 0 war, an der Stelle lcd_flash_string_2 weiter gemacht wird. Im anderen Fall wird die bereits geschriebene Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aufgerufen, welche das Zeichen ausgibt. &#039;&#039;&#039;lcd_data&#039;&#039;&#039; erwartet dabei das Zeichen im Register &#039;&#039;&#039;temp1&#039;&#039;&#039;, genau in dem Register in welches wir vorher mittels &#039;&#039;&#039;lpm&#039;&#039;&#039; das Zeichen geladen hatten.&lt;br /&gt;
&lt;br /&gt;
Das verwendende Programm sieht dann 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 temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
           rcall lcd_init              ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear             ; Display löschen&lt;br /&gt;
 &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;
&lt;br /&gt;
           rcall lcd_flash_string      ; Unterprogramm gibt String aus der&lt;br /&gt;
                                       ; durch den Z-Pointer adressiert wird&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
           .db &amp;quot;Test&amp;quot;,0                ; Stringkonstante, durch eine 0&lt;br /&gt;
                                       ; abgeschlossen  &lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD Funktionen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Genaueres über die Verwendung unterschiedlicher Speicher findet sich im Kapitel [[AVR-Tutorial:_Speicher|Speicher]]&lt;br /&gt;
&lt;br /&gt;
==Zahlen ausgeben==&lt;br /&gt;
Um Zahlen, die beispielsweise in einem Register gespeichert sind, ausgeben zu können, ist es notwendig sich eine Textrepräsentierung der Zahl zu generieren. Die Zahl 123 wird also in den Text &amp;quot;123&amp;quot; umgewandelt welcher dann ausgegeben wird. Aus praktischen Gründen wird allerdings der Text nicht vollständig generiert (man müsste ihn ja irgendwo zwischenspeichern) sondern die einzelnen Buchstaben werden sofort ausgegeben, sobald sie bekannt sind.&lt;br /&gt;
&lt;br /&gt;
===Dezimal ausgeben===&lt;br /&gt;
Das Prinzip der Umwandlung ist einfach. Um herauszufinden wieviele Hunderter in der Zahl 123 enthalten sind, genügt es in einer Schleife immer wieder 100 von der Zahl abzuziehen und mitzuzählen wie oft dies gelang, bevor das Ergebnis negativ wurde. In diesem Fall lautet die Antwort: 1 mal, denn 123 - 100 macht 23. Versucht man erneut 100 anzuziehen, so ergibt sich eine negative Zahl.&lt;br /&gt;
Also muss eine &#039;1&#039; ausgeben werden. Die verbleibenden 23 werden weiterbehandelt, indem festgestellt wird wieviele Zehner darin enthalten sind. Auch hier wiederrum: In einer Schleife solange 10 abziehen, bis das Ergebnis nagativ wurde. Konkret geht das 2 mal gut, also muss das nächste auszugebende Zeichen ein &#039;2&#039; sein. Damit verbleiben noch die Einer, welche direkt in das entsprechende Zeichen umgewandelt werden können. In Summe hat man also an das Display die Zeichen &#039;1&#039; &#039;2&#039; &#039;3&#039; ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number:&lt;br /&gt;
           push  temp2            ; die Funktion verändert temp2, also sichern&lt;br /&gt;
                                  ; wir den Inhalt, um ihn am Ende wieder&lt;br /&gt;
                                  ; herstellen zu können&lt;br /&gt;
&lt;br /&gt;
           mov   temp2, temp1     ; das Register temp1 frei machen&lt;br /&gt;
                                  ; abzählen wieviele Hunderter&lt;br /&gt;
                                  ; in der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
           subi  temp2, 100       ; 100 abziehen&lt;br /&gt;
           brcs  lcd_number_2     ; ist dadurch ein Unterlauf entstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Hunderter mehr ...&lt;br /&gt;
           rjmp  lcd_number_1     ; ... und ab zur nächsten Runde&lt;br /&gt;
;&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorherhgehende Schleife 100 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; abzählen wieviele Zehner in&lt;br /&gt;
                  ; der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_3:&lt;br /&gt;
           subi  temp2, 10        ; 10 abziehen&lt;br /&gt;
           brcs  lcd_number_4     ; ist dadurch ein Unterlauf enstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Zehner mehr ...&lt;br /&gt;
           rjmp  lcd_number_3     ; ... und ab zur nächsten Runde&lt;br /&gt;
&lt;br /&gt;
                              ; die Zehnerstelle ausgeben&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; die übrig gebliebenen Einer&lt;br /&gt;
                  ; noch ausgeben&lt;br /&gt;
           ldi   temp1, &#039;0&#039;       ; die Zahl in temp2 ist jetzt im Bereich&lt;br /&gt;
           add   temp1, temp2     ; 0 bis 9. Einfach nur den ASCII Code für&lt;br /&gt;
           rcall lcd_data         ; &#039;0&#039; dazu addieren und wir erhalten dierekt&lt;br /&gt;
                                  ; den ASCII Code für die Ziffer&lt;br /&gt;
&lt;br /&gt;
           pop   temp2            ; den gesicherten Inhalt von temp2 wieder herstellen&lt;br /&gt;
           ret                    ; und zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beachte: Diese Funktion benutzt wiederrum die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039;. Anders als bei den bisherigen Aufrufen ist &#039;&#039;&#039;lcd_number&#039;&#039;&#039; aber darauf angewiesen, dass &#039;&#039;&#039;lcd_data&#039;&#039;&#039; das Register &#039;&#039;&#039;temp2&#039;&#039;&#039; unangetastet lässt. Falls sie es noch nicht getan haben, dann ist das jetzt die perfekte Gelegenheit, &#039;&#039;&#039;lcd_data&#039;&#039;&#039; mit den entsprechenden &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; Befehlen zu versehen. Sie sollten dies unbedingt zur Übung selbst machen. Am Ende muß die Funktion dann wie diese hier aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1           ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap  temp1                  ; Vertauschen&lt;br /&gt;
           andi  temp1, 0b00001111      ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr   temp1, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp1        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi  temp2, 0b00001111      ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr   temp2, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp2        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur ohne RS zu setzen&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           andi  temp1, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi  temp2, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz zur Funktionsweise der Funktion &#039;&#039;&#039;lcd_number&#039;&#039;&#039;: Die Zahl in einem Register bewegt sich im Wertebereich 0 bis 255. Um herauszufinden, wie die Hunderterstelle lautet, zieht die Funktion einfach in einer Schleife immer wieder 100 von der Schleife ab, bis bei der Subtraktion ein Unterlauf, angezeigt durch das Setzen des Carry-Bits bei der Subtraktion, entsteht. Die Anzahl wird im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; mitgezählt. Da dieses Register mit dem ASCII Code von &#039;0&#039; initialisiert wurde, und dieser ASCII Code bei jedem Schleifendurchlauf um 1 erhöht wird, können wir das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; direkt zur Ausgabe des Zeichens für die Hunderterstelle durch die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; benutzen. Völlig analog funktioniert auch die Ausgabe der Zehnerstelle.&lt;br /&gt;
&lt;br /&gt;
===Unterdrückung von führenden Nullen===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung: Diese Routine ist fehlerhaft&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt jede Zahl im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; immer mit 3 Stellen aus. Führende Nullen werden nicht unterdrückt. Möchte man dies ändern, so ist das ganz leicht möglich: Vor Ausgabe der Hunderterstelle bzw. Zehnerstelle muss lediglich überprüft werden, ob die Entsprechende Ausgabe eine &#039;0&#039; wäre. Ist sie das, so wird die Ausgabe übersprungen. Lediglich in der Einerstelle wird jede Ziffer wie errechnet ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ...&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben, wenn&lt;br /&gt;
                                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_2a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_2a:&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&lt;br /&gt;
           ...&lt;br /&gt;
                              ; die Zehnerstelle ausgeben, wenn&lt;br /&gt;
                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_4a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_4a:&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Verfahren, die einzelnen Stellen durch Subtraktion zu bestimmen, ist bei kleinen Zahlen eine durchaus gängige Alternative. Vor allem dann, wenn keine hardwaremäßige Unterstützung für Multiplikation und Division zur Verfügung steht. Ansonsten könnte man die die einzelnen Ziffern auch durch Division bestimmen. Das Prinzip ist folgendes (beispielhaft an der Zahl 52783 gezeigt)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   52783 / 10          -&amp;gt; 5278&lt;br /&gt;
   52783 - 5278 * 10   -&amp;gt;          3&lt;br /&gt;
&lt;br /&gt;
   5278 / 10           -&amp;gt; 527&lt;br /&gt;
   5278 - 527 * 10     -&amp;gt;          8&lt;br /&gt;
&lt;br /&gt;
   527 / 10            -&amp;gt; 52&lt;br /&gt;
   527 - 52 * 10       -&amp;gt;          7&lt;br /&gt;
&lt;br /&gt;
   52 / 10             -&amp;gt; 5&lt;br /&gt;
   52 - 5 * 10         -&amp;gt;          2&lt;br /&gt;
&lt;br /&gt;
   5 / 10              -&amp;gt; 0&lt;br /&gt;
   5 - 0 * 10          -&amp;gt;          5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Prinzip ist also die Restbildung bei einer fortgesetzten Division durch 10, wobei die einzelnen Ziffern in umgekehrter Reihenfolge ihrer Wertigkeit entstehen. Dadurch hat man aber ein Problem: Damit die Zeichen in der richtigen Reihenfolge ausgegeben werden können, muß man sie meistens zwischenspeichern um sie in der richtigen Reihenfole ausgeben zu können. Wird die Zahl in einem Feld von immer gleicher Größe ausgegeben, dann kann man auch die Zahl von rechts nach links ausgeben (bei einem LCD ist das möglich).&lt;br /&gt;
&lt;br /&gt;
===Hexadezimal ausgeben===&lt;br /&gt;
&lt;br /&gt;
Zu guter letzt hier noch eine Funktion, die eine Zahl aus dem Register &#039;&#039;&#039;temp1&#039;&#039;&#039; in hexadezimaler Form ausgibt. Die Funktion weist keine Besonderheiten auf und sollte unmittelbar verständlich sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen hexadezimal ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number_hex:&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           rcall lcd_number_hex_digit&lt;br /&gt;
           swap  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_number_hex_digit:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
           andi  temp1, $0F&lt;br /&gt;
           cpi   temp1, 10&lt;br /&gt;
           brlt  lcd_number_hex_digit_1&lt;br /&gt;
           subi  temp1, -( &#039;A&#039; - &#039;9&#039; - 1 ) ; es wird subi mit negativer Konstante verwendet, weil es kein addi gibt&lt;br /&gt;
lcd_number_hex_digit_1:&lt;br /&gt;
           subi  temp1, -&#039;0&#039;               ; ditto&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           &lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Eine 16-Bit Zahl aus einem Registerpärchen ausgeben===&lt;br /&gt;
&lt;br /&gt;
Um eine 16 Bit Zahl auszugeben wird wieder das bewährte Schema benutzt die einzelnen Stellen durch Subtraktion abzuzählen. Da es sich hierbei allerdings um eine 16 Bit Zahl handelt, müssen die Subtraktionen als 16-Bit Arithmetik ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 16 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp2 (low Byte) / temp3 (high Byte)&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number16:&lt;br /&gt;
           push  temp1&lt;br /&gt;
           push  temp2&lt;br /&gt;
           push  temp3&lt;br /&gt;
&lt;br /&gt;
; die Zehntausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number0:&lt;br /&gt;
           subi  temp2, low(10000)&lt;br /&gt;
           sbci  temp3, high(10000)&lt;br /&gt;
           brcs  lcd_number1&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number0&lt;br /&gt;
&lt;br /&gt;
; .. und ausgeben&lt;br /&gt;
lcd_number1:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-10000)&lt;br /&gt;
           sbci  temp3, high(-10000)&lt;br /&gt;
&lt;br /&gt;
; die Tausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number2:&lt;br /&gt;
           subi  temp2, low(1000)&lt;br /&gt;
           sbci  temp3, high(1000)&lt;br /&gt;
           brcs  lcd_number3&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number2&lt;br /&gt;
&lt;br /&gt;
; ... und ausgeben&lt;br /&gt;
lcd_number3:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-1000)&lt;br /&gt;
           sbci  temp3, high(-1000)&lt;br /&gt;
&lt;br /&gt;
; Als nächtes kommt die Hunderterstelle drann&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number4:&lt;br /&gt;
           subi  temp2, low(100)&lt;br /&gt;
           sbci  temp3, high(100)&lt;br /&gt;
           brcs  lcd_number5&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number4&lt;br /&gt;
&lt;br /&gt;
; und ausgeben&lt;br /&gt;
lcd_number5:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100&lt;br /&gt;
&lt;br /&gt;
; bleiben noch die Zehner&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number6:&lt;br /&gt;
           subi  temp2, 10&lt;br /&gt;
           brcs  lcd_number7&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number6&lt;br /&gt;
&lt;br /&gt;
; ausgeben ...&lt;br /&gt;
lcd_number7:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10&lt;br /&gt;
&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
           add   temp1, temp2&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
; fertig. Stack wieder aufräumen&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           pop   temp3&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Der überarbeitete, komplette Code==&lt;br /&gt;
&lt;br /&gt;
Hier also die komplett überarbeitete Version der LCD Funktionen.&lt;br /&gt;
&lt;br /&gt;
Die für die Benutzung relevanten Funktionen&lt;br /&gt;
* &#039;&#039;&#039;lcd_init&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_clear&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_home&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_data&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_flash_string&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number_hex&#039;&#039;&#039;&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; übergeben, wobei &#039;&#039;&#039;temp1&#039;&#039;&#039; vom Usercode definiert werden muss.&lt;br /&gt;
&lt;br /&gt;
[[Media:lcd-routines.asm|Download lcd-routines.asm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Stack|&lt;br /&gt;
zurücklink=AVR-Tutorial: Stack|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Interrupts|&lt;br /&gt;
vorlink=AVR-Tutorial: Interrupts}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister SREG) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register temp1 übergeben, wobei temp1 vom Usercode definiert werden muss. &lt;br /&gt;
&lt;br /&gt;
der progger hat vergessen das Z reg in den sub&#039;s lcd_flash_string ...&lt;br /&gt;
zu sichern, aber danke für den Einstig&lt;br /&gt;
&lt;br /&gt;
Lars&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27997</id>
		<title>AVR-Tutorial: LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27997"/>
		<updated>2008-05-16T03:11:55Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Anwendung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kaum ein elektronisches Gerät kommt heutzutage noch ohne ein LCD daher. Ist doch auch praktisch, Informationen im Klartext anzeigen zu können, ohne irgendwelche LEDs blinken zu lassen. Kein Wunder also, dass die häufigste Frage in Mikrocontroller-Foren ist: &amp;quot;Wie kann ich ein LCD anschließen?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-Bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-Bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-Bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-Bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-Bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-Bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4 Bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert. Ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitet hat. Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt, da wir wissen, welche Anschlüsse das LCD benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Pinnummer&amp;lt;BR&amp;gt;LCD || Bezeichnung || Anschluss&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |1 || Vss || GND&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |2 || Vcc || 5V&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |3 || Vee || GND oder [[Potentiometer | Poti]] &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |4 || RS || PD4 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |5 || RW || GND &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |6 || E || PD5 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |7 || DB0 || nicht angeschlossen &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |8 || DB1 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |9 || DB2 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |10 || DB3 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |11 || DB4 || PD0 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |12 || DB5 || PD1 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |13 || DB6 || PD2 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |14 || DB7 || PD3 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |15 || A || Vorsicht! Meistens nicht direkt an +5V anschließbar,&amp;lt;BR&amp;gt;nur über einen Vorwiderstand (z.B. 33&amp;amp;Omega;)!&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |16 || K || GND&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Ok alles ist verbunden. Wenn man jetzt den Strom einschaltet, sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display?&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung des LCDs im 4-Bit-Modus ==&lt;br /&gt;
&lt;br /&gt;
Um ein Byte zu übertragen, muss man es erstmal in die beiden Nibbles zerlegen, die getrennt übertragen werden. Da das obere Nibble (Bit 4 - Bit 7) als erstes übertragen wird, die 4 Datenleitungen jedoch an die vier unteren Bits des Port D angeschlossen sind, muss man die beiden Nibbles des zu übertragenden Bytes erstmal vertauschen. Der AVR kennt dazu praktischerweise einen eigenen Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           swap r16               ; vertauscht die beiden Nibbles von r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus 0b00100101 wird so z.B. 0b01010010. &lt;br /&gt;
&lt;br /&gt;
Jetzt sind die Bits für die erste Phase der Übertragung an der richtigen Stelle. Trotzdem wollen wir das Ergebnis nicht einfach so mit &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; an den Port geben. Um die Hälfte des Bytes, die jetzt nicht an die Datenleitungen des LCDs gegeben wird auf null zu setzen, verwendet man folgenden Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           andi r16, 0b00001111   ; Nur die vier unteren (mit 1 markierten)&lt;br /&gt;
                                  ; Bits werden übernommen, alle anderen werden null&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also: Das obere Nibble wird erst mit dem unteren vertauscht, damit es unten ist. Dann wird das obere (das wir jetzt noch nicht brauchen) auf null gesetzt. &lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem LCD noch mitteilen, ob wir Daten oder Befehle senden wollen. Das machen wir, indem wir das Bit, an dem RS angeschlossen ist (PD4), auf 0 (Befehl senden) oder auf 1 (Daten senden) setzen. Um ein Bit in einem normalen Register zu setzen, gibt es den Befehl sbr (Set Bit in Register). Dieser Befehl unterscheidet sich jedoch von sbi (das nur für IO-Register gilt) dadurch, dass man nicht die Nummer des zu setzenden Bits angibt, sondern eine Bitmaske. Das geht so: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbr r16, 0b00010000     ; Bit 4 setzen, alle anderen Bits bleiben gleich&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RS ist an PD4 angeschlossen. Wenn wir r16 an den Port D ausgeben, ist RS jetzt also high und das LCD erwartet Daten anstatt von Befehlen. &lt;br /&gt;
&lt;br /&gt;
Das Ergebnis können wir jetzt endlich direkt an den Port D übergeben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           out PORTD, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss vorher der Port D auf Ausgang geschalten werden, indem man 0xFF ins Datenrichtungsregister DDRD schreibt. &lt;br /&gt;
&lt;br /&gt;
Um dem LCD zu signalisieren, dass es das an den Datenleitungen anliegende Nibble übernehmen kann, wird die E-Leitung (Enable, an PD5 angeschlossen) auf high und kurz darauf wieder auf low gesetzt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbi PORTD, 5              ; Enable high&lt;br /&gt;
           nop                       ; 3 Taktzyklen warten (&amp;quot;nop&amp;quot; = nichts tun)&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5              ; Enable wieder low&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die eine Hälfte des Bytes wäre damit geschafft! Die andere Hälfte kommt direkt hinterher: Alles, was an der obenstehenden Vorgehensweise geändert werden muss, ist, das &amp;quot;swap&amp;quot; (Vertauschen der beiden Nibbles) wegzulassen.&lt;br /&gt;
&lt;br /&gt;
== Initialisierung des Displays ==&lt;br /&gt;
&lt;br /&gt;
Allerdings gibt es noch ein Problem. Wenn ein LCD eingeschaltet wird, dann läuft es zunächst im 8 Bit Modus. Irgendwie muss das Display initialisiert und auf den 4 Bit Modus umgeschaltet werden, und zwar nur mit den 4 zur Verfügung stehenden Datenleitungen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn es Probleme gibt, dann meistens an diesem Punkt. Die &amp;quot;kompatiblen&amp;quot; Kontroller sind gelegentlich doch nicht 100% identisch. Es lohnt sich, das Datenblatt (siehe Weblinks im Artikel [[LCD]]) genau zu lesen, in welcher Reihenfolge und mit welchen Abständen (Delays) die Initialisierungbefehle gesendet werden. Eine weitere Hilfe können Ansteuerungsbeispiele in Forenbeiträgen geben z.B.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79609#664268 (A) KS0066U oder Ähnliche --- LCD Treiber]&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 4 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Achtung: Im Folgenden sind alle Bytes aus Sicht des LCD-Kontrollers angegeben! Da LCD-seitig nur die Leitungen DB4 - DB7 verwendet werden, ist daher immer nur das höherwertige Nibble gültig. Durch die Art der Verschaltung (DB4 - DB7 wurde auf dem PORT an PD0 bis PD3 angeschlossen) ergibt sich eine Verschiebung, so dass das am Kontroller auszugebende Byte nibblemässig vertauscht ist!&lt;br /&gt;
&lt;br /&gt;
Die Sequenz, aus Sicht des Kontrollers, sieht so aus:&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* $2 ins Steuerregister schreiben (RS = 0), dadurch wird auf 4 Bit Daten umstellt&lt;br /&gt;
* Ab jetzt muss für die Übertragung eines Bytes jeweils zuerst das höherwertige Nibble und dann das niederwertige Nibble übertragen werden, wie oben beschrieben&lt;br /&gt;
* Mit dem Konfigurier-Befehl $20 das Display konfigurieren (4-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 8 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Der Vollständigkeit halber hier noch die notwendige Initialiserungssequenz für den 8 Bit Modus. Da hier die Daten komplett als 1 Byte übertragen werden können, sind einige Klimmzüge wie im 4 Bit Modus nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mit dem Konfigurier-Befehl 0x30 das Display konfigurieren (8-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
== Routinen zur LCD-Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
Die Routinen zur Kommunikation mit dem LCD sehen also so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000 (Anm.1)&lt;br /&gt;
           out PORTD, temp1             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000&lt;br /&gt;
           out PORTD, temp2             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out PORTD, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
 ;&lt;br /&gt;
 ; Bei höherem Takt (&amp;gt;= 8 MHz) kann es notwendig sein, &lt;br /&gt;
 ; vor dem Enable High 1-2 Wartetakte (nop) einzufügen. &lt;br /&gt;
 ; Siehe dazu http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi PORTD, 5                 ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5                 ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
&lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi  temp3,50&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall  delay5ms&lt;br /&gt;
           dec  temp3&lt;br /&gt;
           brne powerupwait&lt;br /&gt;
           ldi temp1, 0b00000011        ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out PORTD, temp1             ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00000010        ; 4bit-Modus einstellen&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00101000        ; 4Bit / 2 Zeilen / 5x8&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00001100        ; Display ein / Cursor aus / kein Blinken&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00000100        ; inkrement / kein Scrollen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi temp1, 0b00000001   ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi temp1, 0b00000010   ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anm.1: Siehe [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
Weitere Funktionen (wie z.B. Cursorposition verändern) sollten mit Hilfe der [[AVR-Tutorial:_LCD#Welche_Befehle_versteht_das_LCD.3F|Befehlscodeliste]] nicht schwer zu realisieren sein. Einfach den Code in temp laden, lcd_command aufrufen und ggf. eine Pause einfügen. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man die LCD-Ansteuerung auch an einen anderen Port des Mikrocontrollers &amp;quot;verschieben&amp;quot;: Wenn das LCD z.B. an Port B angeschlossen ist, dann reicht es, im Programm alle &amp;quot;PORTD&amp;quot; durch &amp;quot;PORTB&amp;quot; und &amp;quot;DDRD&amp;quot; durch &amp;quot;DDRB&amp;quot; zu ersetzen. &lt;br /&gt;
&lt;br /&gt;
Wer eine höhere Taktfrequenz als 4 MHz verwendet, der sollte daran denken, die Dauer der Verzögerungsschleifen anzupassen.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das diese Routinen zur Anzeige von Text verwendet, kann z.B. so aussehen (die Datei lcd-routines.asm muss sich im gleichen Verzeichnis befinden). Nach der Initialisierung wird zuerst der Displayinhalt gelöscht. Um dem LCD ein Zeichen zu schicken, lädt man es in temp1 und ruft die Routine &amp;quot;lcd_data&amp;quot; auf. Das folgende Beispiel zeigt das Wort &amp;quot;Test&amp;quot; auf dem LCD an. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/lcd-test.asm Download lcd-test.asm] &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;
&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, 0xFF    ; Port D = Ausgang&lt;br /&gt;
           out DDRD, temp1&lt;br /&gt;
&lt;br /&gt;
           rcall lcd_init     ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear    ; Display löschen&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;T&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;e&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           &lt;br /&gt;
           ldi temp1, &#039;s&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;t&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD-Routinen werden hier eingefügt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für längere Texte ist die Methode, jedes Zeichen einzeln in das Register zu laden und &amp;quot;lcd_data&amp;quot; aufzurufen natürlich nicht sehr praktisch. Dazu später aber mehr.&lt;br /&gt;
&lt;br /&gt;
Bisher wurden in Register immer irgendwelche Zahlenwerte geladen, aber in diesem Programm kommt plötzlich die Anweisung&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
vor. Wie ist diese zu verstehen? Passiert hier etwas grundlegend anderes als beim Laden einer Zahl in ein Register?&lt;br /&gt;
&lt;br /&gt;
Die Antwort darauf lautet: Nein. Auch hier wird letztendlich nur eine Zahl in ein Register geladen. Der Schlüssel zum Verständnis beruht darauf, dass zum LCD, so wie zu allen Ausgabegeräten, für die Ausgabe von Texten immer nur Zahlen übertragen werden, sog. Codes. Zum Beispiel könnte man vereinbaren, dass ein LCD, wenn es den Ausgabecode 65 erhält, ein &#039;A&#039; anzeigt, bei einem Ausgabecode von 66 ein &#039;B&#039; usw. Naturgemäß gibt es daher viele verschiedene Code-Buchstaben Zuordnungen. Damit hier etwas Ordnung in das potentielle Chaos kommt, hat man sich bereits in der Steinzeit der Programmierung auf bestimmte Codetabellen geeinigt, von denen die verbreitetste sicherlich die ASCII-Zuordnung ist.&lt;br /&gt;
&lt;br /&gt;
==ASCII==&lt;br /&gt;
&lt;br /&gt;
ASCII steht für &#039;&#039;American Standard Code for Information Interchange&#039;&#039; und ist ein standardisierter Code zu Zeichenumsetzung. Die Codetabelle sieht hexadezimal dabei wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;.0&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x1&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x2&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x3&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x4&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x6&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x7&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x8&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x9&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xA&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xB&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xC&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xD&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xE&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xF&amp;lt;/th&amp;gt;&amp;lt;br&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;&#039;&#039;&#039;0x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NUL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SOH&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;STX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EOT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ENQ&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ACK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BEL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;HT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;VT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CR&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SO&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SI&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;&#039;&#039;&#039;1x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DLE&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NAK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SYN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CAN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EM&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SUB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ESC&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;US&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;&#039;&#039;&#039;2x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SP&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;!&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;#&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;%&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;amp;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;(&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;)&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;*&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;+&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;,&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;.&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;3x&#039;&#039;&#039;&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;&amp;lt;td&amp;gt;3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;9&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;=&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;gt;&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;4x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;B&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;C&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;D&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;F&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;G&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;H&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;I&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;J&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;L&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;M&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;N&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;O&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;&#039;&#039;&#039;5x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;P&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;R&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;S&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;T&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;U&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;V&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;W&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;X&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;[&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;\&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;]&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;^&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;6x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;`&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;a&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;b&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;c&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;d&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;e&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;f&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;g&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;h&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;i&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;j&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;k&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;l&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;m&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;n&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;o&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;&#039;&#039;&#039;7x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;p&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;r&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;s&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;t&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;u&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;v&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;w&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;{&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;|&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;~&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DEL&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 ersten beiden Zeilen enthalten die Codes für einige Steuerzeichen, ihre vollständige Beschreibung würde hier zu weit führen. Das Zeichen &#039;&#039;&#039;SP&#039;&#039;&#039; steht für ein &#039;&#039;Space&#039;&#039;, also ein Leerzeichen. &#039;&#039;&#039;BS&#039;&#039;&#039; steht für &#039;&#039;Backspace&#039;&#039;, also ein Zeichen zurück. &#039;&#039;&#039;DEL&#039;&#039;&#039; steht für &#039;&#039;Delete&#039;&#039;, also das Löschen eines Zeichens. &#039;&#039;&#039;CR&#039;&#039;&#039; steht für &#039;&#039;Carriage Return&#039;&#039;, also wörtlich: der Wagenrücklauf (einer Schreibmaschine), während &#039;&#039;&#039;LF&#039;&#039;&#039; für &#039;&#039;Line feed&#039;&#039;, also einen Zeilenvorschub steht.&lt;br /&gt;
&lt;br /&gt;
Der Assembler kennt diese Codetabelle und ersetzt die Zeile&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, $54&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
was letztendlich auch der Lesbarkeit des Programmes zugute kommt. Funktional besteht kein Unterschied zwischen den beiden Anweisungen. Beide bewirken, dass das Register temp1 mit dem Bitmuster 01010100 ( = hexadezimal 54, = dezimal 84 oder eben der ASCII Code für &#039;&#039;&#039;T&#039;&#039;&#039;) geladen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das LCD wiederrum kennt diese Code-Tabelle ebenfalls und wenn es über den Datenbus die Codezahl $54 zur Anzeige empfängt, dann schreibt es ein &#039;&#039;&#039;T&#039;&#039;&#039; an die aktuelle Cursorposition. Genauer gesagt, weiss das LCD nichts von einem &#039;&#039;&#039;T&#039;&#039;&#039;. Es sieht einfach in seinen internen Tabellen nach, welche Pixel beim Empfang der Codezahl $54 auf schwarz zu setzen sind. &#039;Zufällig&#039; sind das genau jene Pixel, die für uns Menschen ein &#039;&#039;&#039;T&#039;&#039;&#039; ergeben.&lt;br /&gt;
&lt;br /&gt;
==Welche Befehle versteht das LCD?==&lt;br /&gt;
&lt;br /&gt;
Auf dem LCD arbeitet ein Kontroller vom Typ HD44780. Diesen Kontroller versteht eine Reihe von Befehlen, die allesamt mittels lcd_command gesendet werden können. Ein Kommando ist dabei nichts anderes als ein Befehlsbyte, indem die verschiedenen Bits verschiedene Bedeutung besitzen:&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 0 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 1 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;der Zustand dieses Bits ist egal&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;sonstige Buchstaben&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;das Bit muss je nach gewünschter Funktionalität gesetzt werden. Die mögliche Funktionalität des jeweiligen Bits geht aus der Befehlsbeschreibung hervor&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Kommando &#039;ON/OFF Control&#039; soll benutzt werden um das Display einzuschalten, der Cursor soll eingeschaltet werden und der Cursor soll blinken.&lt;br /&gt;
Das Befehlsbyte ist so aufgebaut:&lt;br /&gt;
   0b00001dcb&lt;br /&gt;
Aus der Befehlsbeschreibung entnimmt man:&lt;br /&gt;
* Display ein bedeutet, dass an der Bitposition d eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor ein bedeutet, dass an der Bitposition c ein 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor blinken bedeutet, dass an der Bitposition b eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
Das dafür zu übertragende Befehlsbyte hat also die Gestalt 0b00001111 oder in hexadezimaler Schreibweise $0F&lt;br /&gt;
&lt;br /&gt;
===Clear display: 0b00000001===&lt;br /&gt;
&lt;br /&gt;
Die Anzeige wird gelöscht und der Ausgabecursor kehrt an die Home Position (links, erste Zeile) zurück&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Cursor home: 0b0000001x===&lt;br /&gt;
&lt;br /&gt;
Der Cursor kehrt an die Home Position (links, erste Zeile) zurück. Ein verschobenes Display wird auf die Grundeinstellung zurückgesetzt.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs bis 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Entry mode: 0b000001is===&lt;br /&gt;
&lt;br /&gt;
Legt die Cursor Richtung fest sowie eine mögliche Verschiebung des Displays fest&lt;br /&gt;
* i = 1, Cursorposition bei Ausgabe eines Zeichens erhöhen&lt;br /&gt;
* i = 0, Cursorposition bei Ausgabe eines Zeichens vermindern&lt;br /&gt;
* s = 1, Display wird gescrollt, wenn der Cursor das Ende/Anfang, je nach Einstellung von i, erreicht hat.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===On/off control: 0b00001dcb===&lt;br /&gt;
&lt;br /&gt;
Display insgesamt ein/ausschalten; den Cursor ein/ausschalten; den Cursor auf blinken schalten/blinken aus. Wenn das Display ausgeschaltet wird, geht der Inhalt des Displays nicht verloren. Der vorher angezeigte Text wird nach wiedereinschalten erneut angezeigt.&lt;br /&gt;
Ist der Cursor eingeschaltet, aber Blinken ausgeschaltet, so wird der Cursor als Cursorzeile in Pixelzeile 8 dargestellt. Ist Blinken eingeschaltet, wird der Cursor als blinkendes ausgefülltes Rechteck dargestellt, welches abwechselnd mit dem Buchstaben an dieser Stelle angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
* d = 0, Display aus&lt;br /&gt;
* d = 1, Display ein&lt;br /&gt;
* c = 0, Cursor aus&lt;br /&gt;
* c = 1, Cursor ein&lt;br /&gt;
* b = 0, Cursor blinken aus&lt;br /&gt;
* b = 1, Cursor blinken ein&lt;br /&gt;
 &lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Cursor/Scrollen: 0b0001srxx===&lt;br /&gt;
&lt;br /&gt;
Bewegt den Cursor oder scrollt das Display um eine Position entweder nach rechts oder nach links.&lt;br /&gt;
&lt;br /&gt;
* s = 1, Display scrollen&lt;br /&gt;
* s = 0, Cursor bewegen&lt;br /&gt;
* r = 1, nach rechts&lt;br /&gt;
* r = 0, nach links &lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Konfiguration: 0b001dnfxx===&lt;br /&gt;
&lt;br /&gt;
Einstellen der Interface Art, Modus, Font&lt;br /&gt;
* d = 0, 4-Bit Interface&lt;br /&gt;
* d = 1, 8-Bit Interface&lt;br /&gt;
* n = 0, 1 zeilig&lt;br /&gt;
* n = 1, 2 zeilig&lt;br /&gt;
* f = 0, 5x7 Pixel&lt;br /&gt;
* f = 1, 5x11 Pixel&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Character RAM Address Set: 0b01aaaaaa===&lt;br /&gt;
&lt;br /&gt;
Mit diesem Kommando werden maximal 8 selbst definierte Zeichen definiert. Dazu wird der Character RAM Zeiger auf den Anfang des Character Generator (CG) RAM gesetzt und das Zeichen durch die Ausgabe von 8 Byte definiert. Der Adress Zeiger wird nach Ausgabe jeder Pixelzeile (8Bit) vom LCD selbst erhöht. Nach Beendigung der Zeichendefinition muss die Schreibposition explizit mit dem Kommando &amp;quot;Display RAM Address Set&amp;quot; wieder in den DD-RAM Bereich gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
aaaaaa 6-bit CG RAM Adresse&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Display RAM Address Set: 0b1aaaaaaa===&lt;br /&gt;
&lt;br /&gt;
Den Cursor neu positionieren. Display Data (DD) Ram ist vom Character Generator (CG) Ram unabhängig. Der Adresszeiger wird bei Ausgabe eines Zeichens ins DD Ram automatisch erhöht. Das Display verhält sich so, als ob eine Zeile immer aus 32 logischen Zeichen besteht, von der, je nach konkretem Displaytyp (16 Zeichen, 20 Zeichen) immer nur ein Teil sichtbar ist.&lt;br /&gt;
&lt;br /&gt;
aaaaaaa 7-bit DD RAM Adresse. Auf 2-zeiligen Displays (und den meisten 16x1 Displays), kann die Adressangabe wie folgt interpretiert werden&lt;br /&gt;
&lt;br /&gt;
1laaaaaa&lt;br /&gt;
* l = Zeilennummer (0 oder 1)&lt;br /&gt;
* a = 6-Bit Spaltennummer&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
==Einschub: Code aufräumen==&lt;br /&gt;
&lt;br /&gt;
Es wird Zeit sich einmal etwas kritisch mit den bisher geschriebenen Funktionen auseinander zu setzen.&lt;br /&gt;
&lt;br /&gt;
===Portnamen aus dem Code herausziehen===&lt;br /&gt;
&lt;br /&gt;
Wenn wir die LCD-Funktionen einmal genauer betrachten, dann fällt sofort auf, daß über die Funktionen verstreut immer wieder das &#039;&#039;&#039;PORTD&#039;&#039;&#039; sowie einzelne Zahlen für die Pins an diesem Port auftauchen. Wenn das LCD an einem anderen Port betrieben werden soll, oder sich die Pin-Belegung ändert, dann muß an all diesen Stellen eine Anpassung vorgenommen werden. Dabei darf keine einzige Stelle übersehen werden, ansonsten würden die LCD-Funktionen nicht oder nicht vollständig funktionieren.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit dem vorzubeugen ist es, diese immer gleichbleibenden Dinge an den Anfang der LCD-Funktionen vorzuziehen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
.equ PIN_E    = 5&lt;br /&gt;
.equ PIN_RS   = 4&lt;br /&gt;
&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp1          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp2          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi LCD_PORT, PIN_E          ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi LCD_PORT, PIN_E          ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
 &lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf Ausgang&lt;br /&gt;
           out   LCD_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi   temp3,6&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           dec   temp3&lt;br /&gt;
           brne  powerupwait&lt;br /&gt;
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out   LCD_PORT, temp1        ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00101000      ; 4 Bot, 2 Zeilen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00001100      ; Display on, Cursor off&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00000100      ; endlich fertig&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi   temp1, 0b00000010      ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels &#039;&#039;&#039;.equ&#039;&#039;&#039; werden mit dem Assembler Textersetzungen vereinbart. Der Assembler ersetzt alle Vorkomnisse des Quelltextes durch den zu ersetzenden Text. Dadurch ist es zb. möglich alle Vorkommnisse von &#039;&#039;&#039;PORTD&#039;&#039;&#039; durch &#039;&#039;&#039;LCD_PORT&#039;&#039;&#039; auszutauschen. Wird das LCD an einen anderen Port, zb. &#039;&#039;&#039;PORTB&#039;&#039;&#039; gelegt, dann genügt es, einzig und alleine die Zeilen&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTB&lt;br /&gt;
.equ LCD_DDR  = DDRB&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu ersetzen. Der Assembler sorgt dann dafür, dass diese Portänderung an den relevanten Stellen im Code über die Textersetzungen einfliesst. Selbiges natürlich mit der Pin-Zuordnung.&lt;br /&gt;
&lt;br /&gt;
===Registerbenutzung===&lt;br /&gt;
&lt;br /&gt;
Bei diesen Funktionen mussten einige Register des Prozessors benutzt werden um darin Zwischenergebnisse zu speichern bzw. zu bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss dabei natürlich, dass es zu keinen Überschneidungen kommt. Solange nur jede Funktion jeweils für sich betrachtet wird, ist das kein Problem. In 20 oder 30 Code-Zeilen kann man gut verfolgen, welches Register wofür benutzt wird. Schwieriger wird es, wenn Funktionen wiederrum andere Funktionen aufrufen, die ihrerseits wieder Funktionen aufrufen usw. Jede dieser Funktionen benutzt einige Register und mit zunehmender Programmgröße wird es immer schwieriger zu verfolgen, welches Register zu welchem Zeitpunkt wofür benutzt wird.&lt;br /&gt;
&lt;br /&gt;
Speziell bei Basisfunktionen, wie diesen LCD-Funktionen, ist es daher oft ratsam, dafür zu sorgen, daß jede Funktion die Register wieder in dem Zustand hinterlässt, indem sie sie auch vorgefunden hat. Wir benötigen dazu wieder den Stack, auf dem die Registerinhalte bei Betreten einer Funktion zwischengespeichert werden und von dem die Register bei Verlassen einer Funktion wiederhergestellt werden.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir die Funktion&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion verändert das Register temp1. Um das Register abzusichern schreiben wir die Funktion um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           push  temp1                  ; temp1 auf dem Stack sichern&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           pop   temp1                  ; temp1 vom Stack wiederherstellen&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am besten hält man sich an die Regel: Jede Funktion ist dafür zuständig die Register zu sichern und wiederherzustellen, die sie auch selbst verändert. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; ruft die Funktionen &#039;&#039;&#039;lcd_command&#039;&#039;&#039; und &#039;&#039;&#039;delay5ms&#039;&#039;&#039; auf. Wenn diese Funktionen selbst wieder Register verändern (und das tun sie), so ist es die Aufgabe dieser Funktionen, sich um die Sicherung und das Wiederherstellen der entsprechenden Register zu kümmern. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; sollte sich nicht darum kümmern müssen. Auf diese Weise ist das Schlimmste, das einem passieren kann, das ein paar Register unnütz gesichert und wiederhergestellt werden. Das kostet zwar etwas Rechenzeit und etwas Speicherplatz auf dem Stack, ist aber immer noch besser als das andere Extrem: Nach einem Funktionsaufruf haben einige Register nicht mehr den Wert den sie haben sollten und das Programm rechnet mit falschen Zahlen weiter.&lt;br /&gt;
&lt;br /&gt;
===Lass den Assembler rechnen===&lt;br /&gt;
Betrachtet man den Code genauer, so fallen einige konstante Zahlenwerte auf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Das vorangestellte $ kennzeichnet die Zahl als Hexadezimalzahl) Der Code benötigt eine Warteschleife die mindestens 50µs dauert. Die beiden Befehle innerhalb der Schleife benötigen 3 Takte: 1 Takt für den &#039;&#039;&#039;dec&#039;&#039;&#039; und der &#039;&#039;&#039;brne&#039;&#039;&#039; benötigt 2 Takte wenn die Bedingung zutrifft, der Branch also genommen wird. Bei 4 Mhz werden also 4000000 / 3 * 50 / 1000000 = 66.6 Durchläufe durch die Schleife benötigt um eine Verzögerungszeit von 50µs (0.000050 Sekunden) zu erreichen, hexadezimal ausgedrückt: $42&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist: Bei anderen Taktfrequenzen müsste man nun jedesmal diese Berechnung machen und den entsprechenden Zahlenwert einsetzen. Das kann aber der Assembler genausogut erledigen. Am Anfang des Codes wird ein Eintrag definiert, der die Taktfrequenz festlegt. Traditionell heist dieser Eintrag &amp;lt;i&amp;gt;XTAL&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ XTAL  = 4000000&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 50 / 3 ) / 1000000&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An einer anderen Codestelle gibt es weitere derartige magische Zahlen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was geht hier vor?&lt;br /&gt;
Die innere Schleife benötigt wieder 3 Takte pro Durchlauf. Bei $C9 = 201 Durchläufen werden also 201 * 3 = 603 Takte verbraucht. In der äußeren Schleife kommen pro Durchlauf alo 1 + 603 + 1 + 2 = 607 Takte verbraucht. Da die äußere Schleife $21 = 33 mal wiederholt wird, werden 20031 Takte verbraucht. Bei 4Mhz benötigt der Prozessor 20031 / 4000000 = 0.005007 Sekunden, also 5 ms.&lt;br /&gt;
Wird der Wiederholwert für die innere Schleife bei $C9 belassen, so werden 4000000 / 607 * 5 / 1000 Wiederholungen der äusseren Schleife benötigt. Auch diese Berechnung kann wieder der Assembler übernehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 5 / 607 ) / 1000&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein kleines Problem kann bei der Verwendung dieses Verfahrens entstehen: Bei hohen Taktfrequenzen und großen Wartezeiten kann der berechnete Wert größer als 255 werden und man bekommt die Fehlermeldung &amp;quot;Operand(s) out of range&amp;quot; beim Assemblieren. Dieser Fall tritt zum Beispiel für obige Konstruktion bei einer Taktfrequenz von 16 MHz ein (genauer gesagt ab 15,3 MHz), während darunter XTAL beliebig geändert werden kann. Als einfachste Lösung bietet es sich an, die Zahl der Takte pro Schleifendurchlauf durch das Einfügen von &#039;&#039;&#039;nop&#039;&#039;&#039; zu erhöhen und die Berechnungsvorschrift anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Ausgabe eines konstanten Textes ==&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde schon einmal ein Text ausgegeben. Dies geschah durch Ausgabe von einzelnen Zeichen. Das können wir auch anders machen. Wir können den Text im Speicher ablegen und eine Funktion schreiben, die die einzelnen Zeichen aus dem Speicher holt und ausgibt. Dabei erhebt sich aber eine Fragestellung: Woher weiß die Funktion eigentlich, wie lange der Text ist? Die Antwort darauf lautet: Sie kann es nicht wissen. Wir müssen irgendwelche Vereinbarungen treffen, woran die Funktion erkennen kann, dass der Text zu Ende ist. Im Wesentlichen werden dazu 2 Methoden benutzt:&lt;br /&gt;
* Der Text enthält ein spezielles Zeichen, welches das Ende des Textes markiert&lt;br /&gt;
* Wir speichern nicht nur den Text selbst, sondern auch die Länge des Textes&lt;br /&gt;
Mit einer der beiden Methoden ist es der Textausgabefunktion dann ein Leichtes, den Text vollständig auszugeben.&lt;br /&gt;
&lt;br /&gt;
Wir werden uns im Weiteren dafür entscheiden, ein spezielles Zeichen, eine 0, dafür zu benutzen. Die Ausgabefunktionen werden dann etwas einfacher, als wenn bei der Ausgabe die Anzahl der bereits ausgegebenen Zeichen mitgezählt werden muss.&lt;br /&gt;
&lt;br /&gt;
Den Text selbst speichern wir im Flash-Speicher, also dort wo auch das Programm gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Einen konstanten Text aus dem Flash Speicher&lt;br /&gt;
 ; ausgeben. Der Text wird mit einer 0 beendet&lt;br /&gt;
lcd_flash_string:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_1:&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           rjmp  lcd_flash_string_1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_2:&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion benutzt den Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039; um das jeweils nächste Zeichen aus dem Flash Speicher in ein Register zur Weiterverarbeitung zu laden. Dazu wird der sog. &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; benutzt. So nennt man das Registerpaar &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;. Nach jedem Ladevorgang wird dabei durch den Befehl&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
dieser Z-Pointer um 1 erhöht. Mittels &#039;&#039;&#039;cpi&#039;&#039;&#039; wird das in das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; geladene Zeichen mit 0 verglichen. &#039;&#039;&#039;cpi&#039;&#039;&#039; vergleicht die beiden Zahlen und merkt sich das Ergebnis in einem speziellen Register in Form von Status Bits. &#039;&#039;&#039;cpi&#039;&#039;&#039; zieht dabei ganz einfach die beiden Zahlen voneinander ab. Sind sie gleich, so kommt da als Ergebnis 0 heraus und &#039;&#039;&#039;cpi&#039;&#039;&#039; setzt daher konsequenter Weise das Zero-Flag, das anzeigt, daß die vorhergegangene Operation ein 0 Ergebnis hatte.&#039;&#039;&#039;breq&#039;&#039;&#039; wertet diese Status-Bits aus. Wenn die vorhergegangene Operation ein 0-Ergebnis hatte, das Zero-Flag also gesetzt ist, dann wird ein Sprung zum angegebenen Label durchgeführt. In Summe bewirkt also die Sequenz&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
das das gelesene Zeichen mit 0 verglichen wird und falls das gelesene&lt;br /&gt;
Zeichen tatsächlich 0 war, an der Stelle lcd_flash_string_2 weiter gemacht wird. Im anderen Fall wird die bereits geschriebene Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aufgerufen, welche das Zeichen ausgibt. &#039;&#039;&#039;lcd_data&#039;&#039;&#039; erwartet dabei das Zeichen im Register &#039;&#039;&#039;temp1&#039;&#039;&#039;, genau in dem Register in welches wir vorher mittels &#039;&#039;&#039;lpm&#039;&#039;&#039; das Zeichen geladen hatten.&lt;br /&gt;
&lt;br /&gt;
Das verwendende Programm sieht dann 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 temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
           rcall lcd_init              ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear             ; Display löschen&lt;br /&gt;
 &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;
&lt;br /&gt;
           rcall lcd_flash_string      ; Unterprogramm gibt String aus der&lt;br /&gt;
                                       ; durch den Z-Pointer adressiert wird&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
           .db &amp;quot;Test&amp;quot;,0                ; Stringkonstante, durch eine 0&lt;br /&gt;
                                       ; abgeschlossen  &lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD Funktionen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Genaueres über die Verwendung unterschiedlicher Speicher findet sich im Kapitel [[AVR-Tutorial:_Speicher|Speicher]]&lt;br /&gt;
&lt;br /&gt;
==Zahlen ausgeben==&lt;br /&gt;
Um Zahlen, die beispielsweise in einem Register gespeichert sind, ausgeben zu können, ist es notwendig sich eine Textrepräsentierung der Zahl zu generieren. Die Zahl 123 wird also in den Text &amp;quot;123&amp;quot; umgewandelt welcher dann ausgegeben wird. Aus praktischen Gründen wird allerdings der Text nicht vollständig generiert (man müsste ihn ja irgendwo zwischenspeichern) sondern die einzelnen Buchstaben werden sofort ausgegeben, sobald sie bekannt sind.&lt;br /&gt;
&lt;br /&gt;
===Dezimal ausgeben===&lt;br /&gt;
Das Prinzip der Umwandlung ist einfach. Um herauszufinden wieviele Hunderter in der Zahl 123 enthalten sind, genügt es in einer Schleife immer wieder 100 von der Zahl abzuziehen und mitzuzählen wie oft dies gelang, bevor das Ergebnis negativ wurde. In diesem Fall lautet die Antwort: 1 mal, denn 123 - 100 macht 23. Versucht man erneut 100 anzuziehen, so ergibt sich eine negative Zahl.&lt;br /&gt;
Also muss eine &#039;1&#039; ausgeben werden. Die verbleibenden 23 werden weiterbehandelt, indem festgestellt wird wieviele Zehner darin enthalten sind. Auch hier wiederrum: In einer Schleife solange 10 abziehen, bis das Ergebnis nagativ wurde. Konkret geht das 2 mal gut, also muss das nächste auszugebende Zeichen ein &#039;2&#039; sein. Damit verbleiben noch die Einer, welche direkt in das entsprechende Zeichen umgewandelt werden können. In Summe hat man also an das Display die Zeichen &#039;1&#039; &#039;2&#039; &#039;3&#039; ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number:&lt;br /&gt;
           push  temp2            ; die Funktion verändert temp2, also sichern&lt;br /&gt;
                                  ; wir den Inhalt, um ihn am Ende wieder&lt;br /&gt;
                                  ; herstellen zu können&lt;br /&gt;
&lt;br /&gt;
           mov   temp2, temp1     ; das Register temp1 frei machen&lt;br /&gt;
                                  ; abzählen wieviele Hunderter&lt;br /&gt;
                                  ; in der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
           subi  temp2, 100       ; 100 abziehen&lt;br /&gt;
           brcs  lcd_number_2     ; ist dadurch ein Unterlauf entstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Hunderter mehr ...&lt;br /&gt;
           rjmp  lcd_number_1     ; ... und ab zur nächsten Runde&lt;br /&gt;
;&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorherhgehende Schleife 100 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; abzählen wieviele Zehner in&lt;br /&gt;
                  ; der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_3:&lt;br /&gt;
           subi  temp2, 10        ; 10 abziehen&lt;br /&gt;
           brcs  lcd_number_4     ; ist dadurch ein Unterlauf enstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Zehner mehr ...&lt;br /&gt;
           rjmp  lcd_number_3     ; ... und ab zur nächsten Runde&lt;br /&gt;
&lt;br /&gt;
                              ; die Zehnerstelle ausgeben&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; die übrig gebliebenen Einer&lt;br /&gt;
                  ; noch ausgeben&lt;br /&gt;
           ldi   temp1, &#039;0&#039;       ; die Zahl in temp2 ist jetzt im Bereich&lt;br /&gt;
           add   temp1, temp2     ; 0 bis 9. Einfach nur den ASCII Code für&lt;br /&gt;
           rcall lcd_data         ; &#039;0&#039; dazu addieren und wir erhalten dierekt&lt;br /&gt;
                                  ; den ASCII Code für die Ziffer&lt;br /&gt;
&lt;br /&gt;
           pop   temp2            ; den gesicherten Inhalt von temp2 wieder herstellen&lt;br /&gt;
           ret                    ; und zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beachte: Diese Funktion benutzt wiederrum die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039;. Anders als bei den bisherigen Aufrufen ist &#039;&#039;&#039;lcd_number&#039;&#039;&#039; aber darauf angewiesen, dass &#039;&#039;&#039;lcd_data&#039;&#039;&#039; das Register &#039;&#039;&#039;temp2&#039;&#039;&#039; unangetastet lässt. Falls sie es noch nicht getan haben, dann ist das jetzt die perfekte Gelegenheit, &#039;&#039;&#039;lcd_data&#039;&#039;&#039; mit den entsprechenden &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; Befehlen zu versehen. Sie sollten dies unbedingt zur Übung selbst machen. Am Ende muß die Funktion dann wie diese hier aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1           ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap  temp1                  ; Vertauschen&lt;br /&gt;
           andi  temp1, 0b00001111      ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr   temp1, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp1        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi  temp2, 0b00001111      ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr   temp2, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp2        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur ohne RS zu setzen&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           andi  temp1, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi  temp2, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz zur Funktionsweise der Funktion &#039;&#039;&#039;lcd_number&#039;&#039;&#039;: Die Zahl in einem Register bewegt sich im Wertebereich 0 bis 255. Um herauszufinden, wie die Hunderterstelle lautet, zieht die Funktion einfach in einer Schleife immer wieder 100 von der Schleife ab, bis bei der Subtraktion ein Unterlauf, angezeigt durch das Setzen des Carry-Bits bei der Subtraktion, entsteht. Die Anzahl wird im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; mitgezählt. Da dieses Register mit dem ASCII Code von &#039;0&#039; initialisiert wurde, und dieser ASCII Code bei jedem Schleifendurchlauf um 1 erhöht wird, können wir das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; direkt zur Ausgabe des Zeichens für die Hunderterstelle durch die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; benutzen. Völlig analog funktioniert auch die Ausgabe der Zehnerstelle.&lt;br /&gt;
&lt;br /&gt;
===Unterdrückung von führenden Nullen===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung: Diese Routine ist fehlerhaft&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt jede Zahl im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; immer mit 3 Stellen aus. Führende Nullen werden nicht unterdrückt. Möchte man dies ändern, so ist das ganz leicht möglich: Vor Ausgabe der Hunderterstelle bzw. Zehnerstelle muss lediglich überprüft werden, ob die Entsprechende Ausgabe eine &#039;0&#039; wäre. Ist sie das, so wird die Ausgabe übersprungen. Lediglich in der Einerstelle wird jede Ziffer wie errechnet ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ...&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben, wenn&lt;br /&gt;
                                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_2a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_2a:&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&lt;br /&gt;
           ...&lt;br /&gt;
                              ; die Zehnerstelle ausgeben, wenn&lt;br /&gt;
                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_4a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_4a:&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Verfahren, die einzelnen Stellen durch Subtraktion zu bestimmen, ist bei kleinen Zahlen eine durchaus gängige Alternative. Vor allem dann, wenn keine hardwaremäßige Unterstützung für Multiplikation und Division zur Verfügung steht. Ansonsten könnte man die die einzelnen Ziffern auch durch Division bestimmen. Das Prinzip ist folgendes (beispielhaft an der Zahl 52783 gezeigt)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   52783 / 10          -&amp;gt; 5278&lt;br /&gt;
   52783 - 5278 * 10   -&amp;gt;          3&lt;br /&gt;
&lt;br /&gt;
   5278 / 10           -&amp;gt; 527&lt;br /&gt;
   5278 - 527 * 10     -&amp;gt;          8&lt;br /&gt;
&lt;br /&gt;
   527 / 10            -&amp;gt; 52&lt;br /&gt;
   527 - 52 * 10       -&amp;gt;          7&lt;br /&gt;
&lt;br /&gt;
   52 / 10             -&amp;gt; 5&lt;br /&gt;
   52 - 5 * 10         -&amp;gt;          2&lt;br /&gt;
&lt;br /&gt;
   5 / 10              -&amp;gt; 0&lt;br /&gt;
   5 - 0 * 10          -&amp;gt;          5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Prinzip ist also die Restbildung bei einer fortgesetzten Division durch 10, wobei die einzelnen Ziffern in umgekehrter Reihenfolge ihrer Wertigkeit entstehen. Dadurch hat man aber ein Problem: Damit die Zeichen in der richtigen Reihenfolge ausgegeben werden können, muß man sie meistens zwischenspeichern um sie in der richtigen Reihenfole ausgeben zu können. Wird die Zahl in einem Feld von immer gleicher Größe ausgegeben, dann kann man auch die Zahl von rechts nach links ausgeben (bei einem LCD ist das möglich).&lt;br /&gt;
&lt;br /&gt;
===Hexadezimal ausgeben===&lt;br /&gt;
&lt;br /&gt;
Zu guter letzt hier noch eine Funktion, die eine Zahl aus dem Register &#039;&#039;&#039;temp1&#039;&#039;&#039; in hexadezimaler Form ausgibt. Die Funktion weist keine Besonderheiten auf und sollte unmittelbar verständlich sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen hexadezimal ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number_hex:&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           rcall lcd_number_hex_digit&lt;br /&gt;
           swap  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_number_hex_digit:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
           andi  temp1, $0F&lt;br /&gt;
           cpi   temp1, 10&lt;br /&gt;
           brlt  lcd_number_hex_digit_1&lt;br /&gt;
           subi  temp1, -( &#039;A&#039; - &#039;9&#039; - 1 ) ; es wird subi mit negativer Konstante verwendet, weil es kein addi gibt&lt;br /&gt;
lcd_number_hex_digit_1:&lt;br /&gt;
           subi  temp1, -&#039;0&#039;               ; ditto&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           &lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Eine 16-Bit Zahl aus einem Registerpärchen ausgeben===&lt;br /&gt;
&lt;br /&gt;
Um eine 16 Bit Zahl auszugeben wird wieder das bewährte Schema benutzt die einzelnen Stellen durch Subtraktion abzuzählen. Da es sich hierbei allerdings um eine 16 Bit Zahl handelt, müssen die Subtraktionen als 16-Bit Arithmetik ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 16 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp2 (low Byte) / temp3 (high Byte)&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number16:&lt;br /&gt;
           push  temp1&lt;br /&gt;
           push  temp2&lt;br /&gt;
           push  temp3&lt;br /&gt;
&lt;br /&gt;
; die Zehntausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number0:&lt;br /&gt;
           subi  temp2, low(10000)&lt;br /&gt;
           sbci  temp3, high(10000)&lt;br /&gt;
           brcs  lcd_number1&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number0&lt;br /&gt;
&lt;br /&gt;
; .. und ausgeben&lt;br /&gt;
lcd_number1:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-10000)&lt;br /&gt;
           sbci  temp3, high(-10000)&lt;br /&gt;
&lt;br /&gt;
; die Tausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number2:&lt;br /&gt;
           subi  temp2, low(1000)&lt;br /&gt;
           sbci  temp3, high(1000)&lt;br /&gt;
           brcs  lcd_number3&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number2&lt;br /&gt;
&lt;br /&gt;
; ... und ausgeben&lt;br /&gt;
lcd_number3:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-1000)&lt;br /&gt;
           sbci  temp3, high(-1000)&lt;br /&gt;
&lt;br /&gt;
; Als nächtes kommt die Hunderterstelle drann&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number4:&lt;br /&gt;
           subi  temp2, low(100)&lt;br /&gt;
           sbci  temp3, high(100)&lt;br /&gt;
           brcs  lcd_number5&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number4&lt;br /&gt;
&lt;br /&gt;
; und ausgeben&lt;br /&gt;
lcd_number5:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100&lt;br /&gt;
&lt;br /&gt;
; bleiben noch die Zehner&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number6:&lt;br /&gt;
           subi  temp2, 10&lt;br /&gt;
           brcs  lcd_number7&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number6&lt;br /&gt;
&lt;br /&gt;
; ausgeben ...&lt;br /&gt;
lcd_number7:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10&lt;br /&gt;
&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
           add   temp1, temp2&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
; fertig. Stack wieder aufräumen&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           pop   temp3&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Der überarbeitete, komplette Code==&lt;br /&gt;
&lt;br /&gt;
Hier also die komplett überarbeitete Version der LCD Funktionen.&lt;br /&gt;
&lt;br /&gt;
Die für die Benutzung relevanten Funktionen&lt;br /&gt;
* &#039;&#039;&#039;lcd_init&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_clear&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_home&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_data&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_flash_string&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number_hex&#039;&#039;&#039;&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; übergeben, wobei &#039;&#039;&#039;temp1&#039;&#039;&#039; vom Usercode definiert werden muss.&lt;br /&gt;
&lt;br /&gt;
[[Media:lcd-routines.asm|Download lcd-routines.asm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Stack|&lt;br /&gt;
zurücklink=AVR-Tutorial: Stack|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Interrupts|&lt;br /&gt;
vorlink=AVR-Tutorial: Interrupts}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister SREG) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register temp1 übergeben, wobei temp1 vom Usercode definiert werden muss. &lt;br /&gt;
&lt;br /&gt;
der progger hat vergessen das Z reg in den sub&#039;s lcd_flash_string ...&lt;br /&gt;
zu sichern, aber danke für den Einstig&lt;br /&gt;
&lt;br /&gt;
Lars&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27996</id>
		<title>AVR-Tutorial: LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27996"/>
		<updated>2008-05-16T03:08:46Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Routinen zur LCD-Ansteuerung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kaum ein elektronisches Gerät kommt heutzutage noch ohne ein LCD daher. Ist doch auch praktisch, Informationen im Klartext anzeigen zu können, ohne irgendwelche LEDs blinken zu lassen. Kein Wunder also, dass die häufigste Frage in Mikrocontroller-Foren ist: &amp;quot;Wie kann ich ein LCD anschließen?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-Bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-Bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-Bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-Bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-Bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-Bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4 Bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert. Ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitet hat. Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt, da wir wissen, welche Anschlüsse das LCD benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Pinnummer&amp;lt;BR&amp;gt;LCD || Bezeichnung || Anschluss&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |1 || Vss || GND&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |2 || Vcc || 5V&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |3 || Vee || GND oder [[Potentiometer | Poti]] &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |4 || RS || PD4 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |5 || RW || GND &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |6 || E || PD5 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |7 || DB0 || nicht angeschlossen &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |8 || DB1 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |9 || DB2 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |10 || DB3 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |11 || DB4 || PD0 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |12 || DB5 || PD1 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |13 || DB6 || PD2 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |14 || DB7 || PD3 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |15 || A || Vorsicht! Meistens nicht direkt an +5V anschließbar,&amp;lt;BR&amp;gt;nur über einen Vorwiderstand (z.B. 33&amp;amp;Omega;)!&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |16 || K || GND&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Ok alles ist verbunden. Wenn man jetzt den Strom einschaltet, sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display?&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung des LCDs im 4-Bit-Modus ==&lt;br /&gt;
&lt;br /&gt;
Um ein Byte zu übertragen, muss man es erstmal in die beiden Nibbles zerlegen, die getrennt übertragen werden. Da das obere Nibble (Bit 4 - Bit 7) als erstes übertragen wird, die 4 Datenleitungen jedoch an die vier unteren Bits des Port D angeschlossen sind, muss man die beiden Nibbles des zu übertragenden Bytes erstmal vertauschen. Der AVR kennt dazu praktischerweise einen eigenen Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           swap r16               ; vertauscht die beiden Nibbles von r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus 0b00100101 wird so z.B. 0b01010010. &lt;br /&gt;
&lt;br /&gt;
Jetzt sind die Bits für die erste Phase der Übertragung an der richtigen Stelle. Trotzdem wollen wir das Ergebnis nicht einfach so mit &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; an den Port geben. Um die Hälfte des Bytes, die jetzt nicht an die Datenleitungen des LCDs gegeben wird auf null zu setzen, verwendet man folgenden Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           andi r16, 0b00001111   ; Nur die vier unteren (mit 1 markierten)&lt;br /&gt;
                                  ; Bits werden übernommen, alle anderen werden null&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also: Das obere Nibble wird erst mit dem unteren vertauscht, damit es unten ist. Dann wird das obere (das wir jetzt noch nicht brauchen) auf null gesetzt. &lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem LCD noch mitteilen, ob wir Daten oder Befehle senden wollen. Das machen wir, indem wir das Bit, an dem RS angeschlossen ist (PD4), auf 0 (Befehl senden) oder auf 1 (Daten senden) setzen. Um ein Bit in einem normalen Register zu setzen, gibt es den Befehl sbr (Set Bit in Register). Dieser Befehl unterscheidet sich jedoch von sbi (das nur für IO-Register gilt) dadurch, dass man nicht die Nummer des zu setzenden Bits angibt, sondern eine Bitmaske. Das geht so: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbr r16, 0b00010000     ; Bit 4 setzen, alle anderen Bits bleiben gleich&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RS ist an PD4 angeschlossen. Wenn wir r16 an den Port D ausgeben, ist RS jetzt also high und das LCD erwartet Daten anstatt von Befehlen. &lt;br /&gt;
&lt;br /&gt;
Das Ergebnis können wir jetzt endlich direkt an den Port D übergeben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           out PORTD, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss vorher der Port D auf Ausgang geschalten werden, indem man 0xFF ins Datenrichtungsregister DDRD schreibt. &lt;br /&gt;
&lt;br /&gt;
Um dem LCD zu signalisieren, dass es das an den Datenleitungen anliegende Nibble übernehmen kann, wird die E-Leitung (Enable, an PD5 angeschlossen) auf high und kurz darauf wieder auf low gesetzt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbi PORTD, 5              ; Enable high&lt;br /&gt;
           nop                       ; 3 Taktzyklen warten (&amp;quot;nop&amp;quot; = nichts tun)&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5              ; Enable wieder low&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die eine Hälfte des Bytes wäre damit geschafft! Die andere Hälfte kommt direkt hinterher: Alles, was an der obenstehenden Vorgehensweise geändert werden muss, ist, das &amp;quot;swap&amp;quot; (Vertauschen der beiden Nibbles) wegzulassen.&lt;br /&gt;
&lt;br /&gt;
== Initialisierung des Displays ==&lt;br /&gt;
&lt;br /&gt;
Allerdings gibt es noch ein Problem. Wenn ein LCD eingeschaltet wird, dann läuft es zunächst im 8 Bit Modus. Irgendwie muss das Display initialisiert und auf den 4 Bit Modus umgeschaltet werden, und zwar nur mit den 4 zur Verfügung stehenden Datenleitungen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn es Probleme gibt, dann meistens an diesem Punkt. Die &amp;quot;kompatiblen&amp;quot; Kontroller sind gelegentlich doch nicht 100% identisch. Es lohnt sich, das Datenblatt (siehe Weblinks im Artikel [[LCD]]) genau zu lesen, in welcher Reihenfolge und mit welchen Abständen (Delays) die Initialisierungbefehle gesendet werden. Eine weitere Hilfe können Ansteuerungsbeispiele in Forenbeiträgen geben z.B.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79609#664268 (A) KS0066U oder Ähnliche --- LCD Treiber]&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 4 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Achtung: Im Folgenden sind alle Bytes aus Sicht des LCD-Kontrollers angegeben! Da LCD-seitig nur die Leitungen DB4 - DB7 verwendet werden, ist daher immer nur das höherwertige Nibble gültig. Durch die Art der Verschaltung (DB4 - DB7 wurde auf dem PORT an PD0 bis PD3 angeschlossen) ergibt sich eine Verschiebung, so dass das am Kontroller auszugebende Byte nibblemässig vertauscht ist!&lt;br /&gt;
&lt;br /&gt;
Die Sequenz, aus Sicht des Kontrollers, sieht so aus:&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* $2 ins Steuerregister schreiben (RS = 0), dadurch wird auf 4 Bit Daten umstellt&lt;br /&gt;
* Ab jetzt muss für die Übertragung eines Bytes jeweils zuerst das höherwertige Nibble und dann das niederwertige Nibble übertragen werden, wie oben beschrieben&lt;br /&gt;
* Mit dem Konfigurier-Befehl $20 das Display konfigurieren (4-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 8 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Der Vollständigkeit halber hier noch die notwendige Initialiserungssequenz für den 8 Bit Modus. Da hier die Daten komplett als 1 Byte übertragen werden können, sind einige Klimmzüge wie im 4 Bit Modus nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mit dem Konfigurier-Befehl 0x30 das Display konfigurieren (8-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
== Routinen zur LCD-Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
Die Routinen zur Kommunikation mit dem LCD sehen also so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000 (Anm.1)&lt;br /&gt;
           out PORTD, temp1             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000&lt;br /&gt;
           out PORTD, temp2             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out PORTD, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
 ;&lt;br /&gt;
 ; Bei höherem Takt (&amp;gt;= 8 MHz) kann es notwendig sein, &lt;br /&gt;
 ; vor dem Enable High 1-2 Wartetakte (nop) einzufügen. &lt;br /&gt;
 ; Siehe dazu http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi PORTD, 5                 ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5                 ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
&lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi  temp3,50&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall  delay5ms&lt;br /&gt;
           dec  temp3&lt;br /&gt;
           brne powerupwait&lt;br /&gt;
           ldi temp1, 0b00000011        ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out PORTD, temp1             ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00000010        ; 4bit-Modus einstellen&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00101000        ; 4Bit / 2 Zeilen / 5x8&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00001100        ; Display ein / Cursor aus / kein Blinken&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00000100        ; inkrement / kein Scrollen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi temp1, 0b00000001   ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi temp1, 0b00000010   ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anm.1: Siehe [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
Weitere Funktionen (wie z.B. Cursorposition verändern) sollten mit Hilfe der [[AVR-Tutorial:_LCD#Welche_Befehle_versteht_das_LCD.3F|Befehlscodeliste]] nicht schwer zu realisieren sein. Einfach den Code in temp laden, lcd_command aufrufen und ggf. eine Pause einfügen. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man die LCD-Ansteuerung auch an einen anderen Port des Mikrocontrollers &amp;quot;verschieben&amp;quot;: Wenn das LCD z.B. an Port B angeschlossen ist, dann reicht es, im Programm alle &amp;quot;PORTD&amp;quot; durch &amp;quot;PORTB&amp;quot; und &amp;quot;DDRD&amp;quot; durch &amp;quot;DDRB&amp;quot; zu ersetzen. &lt;br /&gt;
&lt;br /&gt;
Wer eine höhere Taktfrequenz als 4 MHz verwendet, der sollte daran denken, die Dauer der Verzögerungsschleifen anzupassen.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das diese Routinen zur Anzeige von Text verwendet, kann z.B. so aussehen (die Datei lcd-routines.asm muss sich im gleichen Verzeichnis befinden). Nach der Initialisierung wird zuerst der Displayinhalt gelöscht. Um dem LCD ein Zeichen zu schicken, lädt man es in temp1 und ruft die Routine &amp;quot;lcd_data&amp;quot; auf. Das folgende Beispiel zeigt das Wort &amp;quot;Test&amp;quot; auf dem LCD an. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/lcd-test.asm Download lcd-test.asm] &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;
&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, 0xFF    ; Port D = Ausgang&lt;br /&gt;
           out DDRD, temp1&lt;br /&gt;
&lt;br /&gt;
           rcall lcd_init     ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear    ; Display löschen&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;T&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;e&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           &lt;br /&gt;
           ldi temp1, &#039;s&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;t&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD-Routinen werden hier eingefügt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für längere Texte ist die Methode, jedes Zeichen einzeln in das Register zu laden und &amp;quot;lcd_data&amp;quot; aufzurufen natürlich nicht sehr praktisch. Dazu später aber mehr.&lt;br /&gt;
&lt;br /&gt;
Bisher wurden in Register immer irgendwelche Zahlenwerte geladen, aber in diesem Programm kommt plötzlich die Anweisung&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
vor. Wie ist diese zu verstehen? Passiert hier etwas grundlegend anderes als beim Laden einer Zahl in ein Register?&lt;br /&gt;
&lt;br /&gt;
Die Antwort darauf lautet: Nein. Auch hier wird letztendlich nur eine Zahl in ein Register geladen. Der Schlüssel zum Verständnis beruht darauf, dass zum LCD, so wie zu allen Ausgabegeräten, für die Ausgabe von Texten immer nur Zahlen übertragen werden, sog. Codes. Zum Beispiel könnte man vereinbaren, dass ein LCD, wenn es den Ausgabecode 65 erhält ein &#039;A&#039; anzeigt, bei einem Ausgabecode von 66 ein &#039;B&#039; usw. Naturgemäß gibt es daher viele verschiedene Code-Buchstaben Zuordnungen. Damit hier etwas Ordnung in das potentielle Chaos kommt, hat man sich bereits in der Steinzeit der Programmierung auf bestimmte Codetabellen geeinigt, von denen die verbreitetste sicherlich die ASCII-Zuordnung ist.&lt;br /&gt;
&lt;br /&gt;
==ASCII==&lt;br /&gt;
&lt;br /&gt;
ASCII steht für &#039;&#039;American Standard Code for Information Interchange&#039;&#039; und ist ein standardisierter Code zu Zeichenumsetzung. Die Codetabelle sieht hexadezimal dabei wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;.0&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x1&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x2&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x3&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x4&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x6&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x7&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x8&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x9&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xA&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xB&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xC&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xD&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xE&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xF&amp;lt;/th&amp;gt;&amp;lt;br&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;&#039;&#039;&#039;0x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NUL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SOH&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;STX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EOT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ENQ&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ACK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BEL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;HT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;VT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CR&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SO&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SI&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;&#039;&#039;&#039;1x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DLE&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NAK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SYN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CAN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EM&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SUB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ESC&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;US&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;&#039;&#039;&#039;2x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SP&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;!&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;#&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;%&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;amp;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;(&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;)&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;*&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;+&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;,&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;.&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;3x&#039;&#039;&#039;&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;&amp;lt;td&amp;gt;3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;9&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;=&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;gt;&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;4x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;B&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;C&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;D&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;F&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;G&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;H&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;I&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;J&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;L&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;M&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;N&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;O&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;&#039;&#039;&#039;5x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;P&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;R&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;S&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;T&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;U&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;V&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;W&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;X&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;[&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;\&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;]&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;^&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;6x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;`&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;a&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;b&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;c&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;d&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;e&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;f&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;g&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;h&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;i&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;j&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;k&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;l&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;m&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;n&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;o&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;&#039;&#039;&#039;7x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;p&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;r&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;s&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;t&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;u&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;v&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;w&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;{&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;|&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;~&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DEL&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 ersten beiden Zeilen enthalten die Codes für einige Steuerzeichen, ihre vollständige Beschreibung würde hier zu weit führen. Das Zeichen &#039;&#039;&#039;SP&#039;&#039;&#039; steht für ein &#039;&#039;Space&#039;&#039;, also ein Leerzeichen. &#039;&#039;&#039;BS&#039;&#039;&#039; steht für &#039;&#039;Backspace&#039;&#039;, also ein Zeichen zurück. &#039;&#039;&#039;DEL&#039;&#039;&#039; steht für &#039;&#039;Delete&#039;&#039;, also das Löschen eines Zeichens. &#039;&#039;&#039;CR&#039;&#039;&#039; steht für &#039;&#039;Carriage Return&#039;&#039;, also wörtlich: der Wagenrücklauf (einer Schreibmaschine), während &#039;&#039;&#039;LF&#039;&#039;&#039; für &#039;&#039;Line feed&#039;&#039;, also einen Zeilenvorschub steht.&lt;br /&gt;
&lt;br /&gt;
Der Assembler kennt diese Codetabelle und ersetzt die Zeile&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, $54&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
was letztendlich auch der Lesbarkeit des Programmes zugute kommt. Funktional besteht kein Unterschied zwischen den beiden Anweisungen. Beide bewirken, dass das Register temp1 mit dem Bitmuster 01010100 ( = hexadezimal 54, = dezimal 84 oder eben der ASCII Code für &#039;&#039;&#039;T&#039;&#039;&#039;) geladen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das LCD wiederrum kennt diese Code-Tabelle ebenfalls und wenn es über den Datenbus die Codezahl $54 zur Anzeige empfängt, dann schreibt es ein &#039;&#039;&#039;T&#039;&#039;&#039; an die aktuelle Cursorposition. Genauer gesagt, weiss das LCD nichts von einem &#039;&#039;&#039;T&#039;&#039;&#039;. Es sieht einfach in seinen internen Tabellen nach, welche Pixel beim Empfang der Codezahl $54 auf schwarz zu setzen sind. &#039;Zufällig&#039; sind das genau jene Pixel, die für uns Menschen ein &#039;&#039;&#039;T&#039;&#039;&#039; ergeben.&lt;br /&gt;
&lt;br /&gt;
==Welche Befehle versteht das LCD?==&lt;br /&gt;
&lt;br /&gt;
Auf dem LCD arbeitet ein Kontroller vom Typ HD44780. Diesen Kontroller versteht eine Reihe von Befehlen, die allesamt mittels lcd_command gesendet werden können. Ein Kommando ist dabei nichts anderes als ein Befehlsbyte, indem die verschiedenen Bits verschiedene Bedeutung besitzen:&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 0 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 1 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;der Zustand dieses Bits ist egal&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;sonstige Buchstaben&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;das Bit muss je nach gewünschter Funktionalität gesetzt werden. Die mögliche Funktionalität des jeweiligen Bits geht aus der Befehlsbeschreibung hervor&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Kommando &#039;ON/OFF Control&#039; soll benutzt werden um das Display einzuschalten, der Cursor soll eingeschaltet werden und der Cursor soll blinken.&lt;br /&gt;
Das Befehlsbyte ist so aufgebaut:&lt;br /&gt;
   0b00001dcb&lt;br /&gt;
Aus der Befehlsbeschreibung entnimmt man:&lt;br /&gt;
* Display ein bedeutet, dass an der Bitposition d eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor ein bedeutet, dass an der Bitposition c ein 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor blinken bedeutet, dass an der Bitposition b eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
Das dafür zu übertragende Befehlsbyte hat also die Gestalt 0b00001111 oder in hexadezimaler Schreibweise $0F&lt;br /&gt;
&lt;br /&gt;
===Clear display: 0b00000001===&lt;br /&gt;
&lt;br /&gt;
Die Anzeige wird gelöscht und der Ausgabecursor kehrt an die Home Position (links, erste Zeile) zurück&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Cursor home: 0b0000001x===&lt;br /&gt;
&lt;br /&gt;
Der Cursor kehrt an die Home Position (links, erste Zeile) zurück. Ein verschobenes Display wird auf die Grundeinstellung zurückgesetzt.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs bis 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Entry mode: 0b000001is===&lt;br /&gt;
&lt;br /&gt;
Legt die Cursor Richtung fest sowie eine mögliche Verschiebung des Displays fest&lt;br /&gt;
* i = 1, Cursorposition bei Ausgabe eines Zeichens erhöhen&lt;br /&gt;
* i = 0, Cursorposition bei Ausgabe eines Zeichens vermindern&lt;br /&gt;
* s = 1, Display wird gescrollt, wenn der Cursor das Ende/Anfang, je nach Einstellung von i, erreicht hat.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===On/off control: 0b00001dcb===&lt;br /&gt;
&lt;br /&gt;
Display insgesamt ein/ausschalten; den Cursor ein/ausschalten; den Cursor auf blinken schalten/blinken aus. Wenn das Display ausgeschaltet wird, geht der Inhalt des Displays nicht verloren. Der vorher angezeigte Text wird nach wiedereinschalten erneut angezeigt.&lt;br /&gt;
Ist der Cursor eingeschaltet, aber Blinken ausgeschaltet, so wird der Cursor als Cursorzeile in Pixelzeile 8 dargestellt. Ist Blinken eingeschaltet, wird der Cursor als blinkendes ausgefülltes Rechteck dargestellt, welches abwechselnd mit dem Buchstaben an dieser Stelle angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
* d = 0, Display aus&lt;br /&gt;
* d = 1, Display ein&lt;br /&gt;
* c = 0, Cursor aus&lt;br /&gt;
* c = 1, Cursor ein&lt;br /&gt;
* b = 0, Cursor blinken aus&lt;br /&gt;
* b = 1, Cursor blinken ein&lt;br /&gt;
 &lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Cursor/Scrollen: 0b0001srxx===&lt;br /&gt;
&lt;br /&gt;
Bewegt den Cursor oder scrollt das Display um eine Position entweder nach rechts oder nach links.&lt;br /&gt;
&lt;br /&gt;
* s = 1, Display scrollen&lt;br /&gt;
* s = 0, Cursor bewegen&lt;br /&gt;
* r = 1, nach rechts&lt;br /&gt;
* r = 0, nach links &lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Konfiguration: 0b001dnfxx===&lt;br /&gt;
&lt;br /&gt;
Einstellen der Interface Art, Modus, Font&lt;br /&gt;
* d = 0, 4-Bit Interface&lt;br /&gt;
* d = 1, 8-Bit Interface&lt;br /&gt;
* n = 0, 1 zeilig&lt;br /&gt;
* n = 1, 2 zeilig&lt;br /&gt;
* f = 0, 5x7 Pixel&lt;br /&gt;
* f = 1, 5x11 Pixel&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Character RAM Address Set: 0b01aaaaaa===&lt;br /&gt;
&lt;br /&gt;
Mit diesem Kommando werden maximal 8 selbst definierte Zeichen definiert. Dazu wird der Character RAM Zeiger auf den Anfang des Character Generator (CG) RAM gesetzt und das Zeichen durch die Ausgabe von 8 Byte definiert. Der Adress Zeiger wird nach Ausgabe jeder Pixelzeile (8Bit) vom LCD selbst erhöht. Nach Beendigung der Zeichendefinition muss die Schreibposition explizit mit dem Kommando &amp;quot;Display RAM Address Set&amp;quot; wieder in den DD-RAM Bereich gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
aaaaaa 6-bit CG RAM Adresse&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Display RAM Address Set: 0b1aaaaaaa===&lt;br /&gt;
&lt;br /&gt;
Den Cursor neu positionieren. Display Data (DD) Ram ist vom Character Generator (CG) Ram unabhängig. Der Adresszeiger wird bei Ausgabe eines Zeichens ins DD Ram automatisch erhöht. Das Display verhält sich so, als ob eine Zeile immer aus 32 logischen Zeichen besteht, von der, je nach konkretem Displaytyp (16 Zeichen, 20 Zeichen) immer nur ein Teil sichtbar ist.&lt;br /&gt;
&lt;br /&gt;
aaaaaaa 7-bit DD RAM Adresse. Auf 2-zeiligen Displays (und den meisten 16x1 Displays), kann die Adressangabe wie folgt interpretiert werden&lt;br /&gt;
&lt;br /&gt;
1laaaaaa&lt;br /&gt;
* l = Zeilennummer (0 oder 1)&lt;br /&gt;
* a = 6-Bit Spaltennummer&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
==Einschub: Code aufräumen==&lt;br /&gt;
&lt;br /&gt;
Es wird Zeit sich einmal etwas kritisch mit den bisher geschriebenen Funktionen auseinander zu setzen.&lt;br /&gt;
&lt;br /&gt;
===Portnamen aus dem Code herausziehen===&lt;br /&gt;
&lt;br /&gt;
Wenn wir die LCD-Funktionen einmal genauer betrachten, dann fällt sofort auf, daß über die Funktionen verstreut immer wieder das &#039;&#039;&#039;PORTD&#039;&#039;&#039; sowie einzelne Zahlen für die Pins an diesem Port auftauchen. Wenn das LCD an einem anderen Port betrieben werden soll, oder sich die Pin-Belegung ändert, dann muß an all diesen Stellen eine Anpassung vorgenommen werden. Dabei darf keine einzige Stelle übersehen werden, ansonsten würden die LCD-Funktionen nicht oder nicht vollständig funktionieren.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit dem vorzubeugen ist es, diese immer gleichbleibenden Dinge an den Anfang der LCD-Funktionen vorzuziehen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
.equ PIN_E    = 5&lt;br /&gt;
.equ PIN_RS   = 4&lt;br /&gt;
&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp1          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp2          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi LCD_PORT, PIN_E          ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi LCD_PORT, PIN_E          ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
 &lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf Ausgang&lt;br /&gt;
           out   LCD_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi   temp3,6&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           dec   temp3&lt;br /&gt;
           brne  powerupwait&lt;br /&gt;
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out   LCD_PORT, temp1        ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00101000      ; 4 Bot, 2 Zeilen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00001100      ; Display on, Cursor off&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00000100      ; endlich fertig&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi   temp1, 0b00000010      ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels &#039;&#039;&#039;.equ&#039;&#039;&#039; werden mit dem Assembler Textersetzungen vereinbart. Der Assembler ersetzt alle Vorkomnisse des Quelltextes durch den zu ersetzenden Text. Dadurch ist es zb. möglich alle Vorkommnisse von &#039;&#039;&#039;PORTD&#039;&#039;&#039; durch &#039;&#039;&#039;LCD_PORT&#039;&#039;&#039; auszutauschen. Wird das LCD an einen anderen Port, zb. &#039;&#039;&#039;PORTB&#039;&#039;&#039; gelegt, dann genügt es, einzig und alleine die Zeilen&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTB&lt;br /&gt;
.equ LCD_DDR  = DDRB&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu ersetzen. Der Assembler sorgt dann dafür, dass diese Portänderung an den relevanten Stellen im Code über die Textersetzungen einfliesst. Selbiges natürlich mit der Pin-Zuordnung.&lt;br /&gt;
&lt;br /&gt;
===Registerbenutzung===&lt;br /&gt;
&lt;br /&gt;
Bei diesen Funktionen mussten einige Register des Prozessors benutzt werden um darin Zwischenergebnisse zu speichern bzw. zu bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss dabei natürlich, dass es zu keinen Überschneidungen kommt. Solange nur jede Funktion jeweils für sich betrachtet wird, ist das kein Problem. In 20 oder 30 Code-Zeilen kann man gut verfolgen, welches Register wofür benutzt wird. Schwieriger wird es, wenn Funktionen wiederrum andere Funktionen aufrufen, die ihrerseits wieder Funktionen aufrufen usw. Jede dieser Funktionen benutzt einige Register und mit zunehmender Programmgröße wird es immer schwieriger zu verfolgen, welches Register zu welchem Zeitpunkt wofür benutzt wird.&lt;br /&gt;
&lt;br /&gt;
Speziell bei Basisfunktionen, wie diesen LCD-Funktionen, ist es daher oft ratsam, dafür zu sorgen, daß jede Funktion die Register wieder in dem Zustand hinterlässt, indem sie sie auch vorgefunden hat. Wir benötigen dazu wieder den Stack, auf dem die Registerinhalte bei Betreten einer Funktion zwischengespeichert werden und von dem die Register bei Verlassen einer Funktion wiederhergestellt werden.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir die Funktion&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion verändert das Register temp1. Um das Register abzusichern schreiben wir die Funktion um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           push  temp1                  ; temp1 auf dem Stack sichern&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           pop   temp1                  ; temp1 vom Stack wiederherstellen&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am besten hält man sich an die Regel: Jede Funktion ist dafür zuständig die Register zu sichern und wiederherzustellen, die sie auch selbst verändert. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; ruft die Funktionen &#039;&#039;&#039;lcd_command&#039;&#039;&#039; und &#039;&#039;&#039;delay5ms&#039;&#039;&#039; auf. Wenn diese Funktionen selbst wieder Register verändern (und das tun sie), so ist es die Aufgabe dieser Funktionen, sich um die Sicherung und das Wiederherstellen der entsprechenden Register zu kümmern. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; sollte sich nicht darum kümmern müssen. Auf diese Weise ist das Schlimmste, das einem passieren kann, das ein paar Register unnütz gesichert und wiederhergestellt werden. Das kostet zwar etwas Rechenzeit und etwas Speicherplatz auf dem Stack, ist aber immer noch besser als das andere Extrem: Nach einem Funktionsaufruf haben einige Register nicht mehr den Wert den sie haben sollten und das Programm rechnet mit falschen Zahlen weiter.&lt;br /&gt;
&lt;br /&gt;
===Lass den Assembler rechnen===&lt;br /&gt;
Betrachtet man den Code genauer, so fallen einige konstante Zahlenwerte auf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Das vorangestellte $ kennzeichnet die Zahl als Hexadezimalzahl) Der Code benötigt eine Warteschleife die mindestens 50µs dauert. Die beiden Befehle innerhalb der Schleife benötigen 3 Takte: 1 Takt für den &#039;&#039;&#039;dec&#039;&#039;&#039; und der &#039;&#039;&#039;brne&#039;&#039;&#039; benötigt 2 Takte wenn die Bedingung zutrifft, der Branch also genommen wird. Bei 4 Mhz werden also 4000000 / 3 * 50 / 1000000 = 66.6 Durchläufe durch die Schleife benötigt um eine Verzögerungszeit von 50µs (0.000050 Sekunden) zu erreichen, hexadezimal ausgedrückt: $42&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist: Bei anderen Taktfrequenzen müsste man nun jedesmal diese Berechnung machen und den entsprechenden Zahlenwert einsetzen. Das kann aber der Assembler genausogut erledigen. Am Anfang des Codes wird ein Eintrag definiert, der die Taktfrequenz festlegt. Traditionell heist dieser Eintrag &amp;lt;i&amp;gt;XTAL&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ XTAL  = 4000000&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 50 / 3 ) / 1000000&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An einer anderen Codestelle gibt es weitere derartige magische Zahlen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was geht hier vor?&lt;br /&gt;
Die innere Schleife benötigt wieder 3 Takte pro Durchlauf. Bei $C9 = 201 Durchläufen werden also 201 * 3 = 603 Takte verbraucht. In der äußeren Schleife kommen pro Durchlauf alo 1 + 603 + 1 + 2 = 607 Takte verbraucht. Da die äußere Schleife $21 = 33 mal wiederholt wird, werden 20031 Takte verbraucht. Bei 4Mhz benötigt der Prozessor 20031 / 4000000 = 0.005007 Sekunden, also 5 ms.&lt;br /&gt;
Wird der Wiederholwert für die innere Schleife bei $C9 belassen, so werden 4000000 / 607 * 5 / 1000 Wiederholungen der äusseren Schleife benötigt. Auch diese Berechnung kann wieder der Assembler übernehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 5 / 607 ) / 1000&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein kleines Problem kann bei der Verwendung dieses Verfahrens entstehen: Bei hohen Taktfrequenzen und großen Wartezeiten kann der berechnete Wert größer als 255 werden und man bekommt die Fehlermeldung &amp;quot;Operand(s) out of range&amp;quot; beim Assemblieren. Dieser Fall tritt zum Beispiel für obige Konstruktion bei einer Taktfrequenz von 16 MHz ein (genauer gesagt ab 15,3 MHz), während darunter XTAL beliebig geändert werden kann. Als einfachste Lösung bietet es sich an, die Zahl der Takte pro Schleifendurchlauf durch das Einfügen von &#039;&#039;&#039;nop&#039;&#039;&#039; zu erhöhen und die Berechnungsvorschrift anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Ausgabe eines konstanten Textes ==&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde schon einmal ein Text ausgegeben. Dies geschah durch Ausgabe von einzelnen Zeichen. Das können wir auch anders machen. Wir können den Text im Speicher ablegen und eine Funktion schreiben, die die einzelnen Zeichen aus dem Speicher holt und ausgibt. Dabei erhebt sich aber eine Fragestellung: Woher weiß die Funktion eigentlich, wie lange der Text ist? Die Antwort darauf lautet: Sie kann es nicht wissen. Wir müssen irgendwelche Vereinbarungen treffen, woran die Funktion erkennen kann, dass der Text zu Ende ist. Im Wesentlichen werden dazu 2 Methoden benutzt:&lt;br /&gt;
* Der Text enthält ein spezielles Zeichen, welches das Ende des Textes markiert&lt;br /&gt;
* Wir speichern nicht nur den Text selbst, sondern auch die Länge des Textes&lt;br /&gt;
Mit einer der beiden Methoden ist es der Textausgabefunktion dann ein Leichtes, den Text vollständig auszugeben.&lt;br /&gt;
&lt;br /&gt;
Wir werden uns im Weiteren dafür entscheiden, ein spezielles Zeichen, eine 0, dafür zu benutzen. Die Ausgabefunktionen werden dann etwas einfacher, als wenn bei der Ausgabe die Anzahl der bereits ausgegebenen Zeichen mitgezählt werden muss.&lt;br /&gt;
&lt;br /&gt;
Den Text selbst speichern wir im Flash-Speicher, also dort wo auch das Programm gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Einen konstanten Text aus dem Flash Speicher&lt;br /&gt;
 ; ausgeben. Der Text wird mit einer 0 beendet&lt;br /&gt;
lcd_flash_string:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_1:&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           rjmp  lcd_flash_string_1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_2:&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion benutzt den Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039; um das jeweils nächste Zeichen aus dem Flash Speicher in ein Register zur Weiterverarbeitung zu laden. Dazu wird der sog. &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; benutzt. So nennt man das Registerpaar &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;. Nach jedem Ladevorgang wird dabei durch den Befehl&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
dieser Z-Pointer um 1 erhöht. Mittels &#039;&#039;&#039;cpi&#039;&#039;&#039; wird das in das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; geladene Zeichen mit 0 verglichen. &#039;&#039;&#039;cpi&#039;&#039;&#039; vergleicht die beiden Zahlen und merkt sich das Ergebnis in einem speziellen Register in Form von Status Bits. &#039;&#039;&#039;cpi&#039;&#039;&#039; zieht dabei ganz einfach die beiden Zahlen voneinander ab. Sind sie gleich, so kommt da als Ergebnis 0 heraus und &#039;&#039;&#039;cpi&#039;&#039;&#039; setzt daher konsequenter Weise das Zero-Flag, das anzeigt, daß die vorhergegangene Operation ein 0 Ergebnis hatte.&#039;&#039;&#039;breq&#039;&#039;&#039; wertet diese Status-Bits aus. Wenn die vorhergegangene Operation ein 0-Ergebnis hatte, das Zero-Flag also gesetzt ist, dann wird ein Sprung zum angegebenen Label durchgeführt. In Summe bewirkt also die Sequenz&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
das das gelesene Zeichen mit 0 verglichen wird und falls das gelesene&lt;br /&gt;
Zeichen tatsächlich 0 war, an der Stelle lcd_flash_string_2 weiter gemacht wird. Im anderen Fall wird die bereits geschriebene Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aufgerufen, welche das Zeichen ausgibt. &#039;&#039;&#039;lcd_data&#039;&#039;&#039; erwartet dabei das Zeichen im Register &#039;&#039;&#039;temp1&#039;&#039;&#039;, genau in dem Register in welches wir vorher mittels &#039;&#039;&#039;lpm&#039;&#039;&#039; das Zeichen geladen hatten.&lt;br /&gt;
&lt;br /&gt;
Das verwendende Programm sieht dann 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 temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
           rcall lcd_init              ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear             ; Display löschen&lt;br /&gt;
 &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;
&lt;br /&gt;
           rcall lcd_flash_string      ; Unterprogramm gibt String aus der&lt;br /&gt;
                                       ; durch den Z-Pointer adressiert wird&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
           .db &amp;quot;Test&amp;quot;,0                ; Stringkonstante, durch eine 0&lt;br /&gt;
                                       ; abgeschlossen  &lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD Funktionen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Genaueres über die Verwendung unterschiedlicher Speicher findet sich im Kapitel [[AVR-Tutorial:_Speicher|Speicher]]&lt;br /&gt;
&lt;br /&gt;
==Zahlen ausgeben==&lt;br /&gt;
Um Zahlen, die beispielsweise in einem Register gespeichert sind, ausgeben zu können, ist es notwendig sich eine Textrepräsentierung der Zahl zu generieren. Die Zahl 123 wird also in den Text &amp;quot;123&amp;quot; umgewandelt welcher dann ausgegeben wird. Aus praktischen Gründen wird allerdings der Text nicht vollständig generiert (man müsste ihn ja irgendwo zwischenspeichern) sondern die einzelnen Buchstaben werden sofort ausgegeben, sobald sie bekannt sind.&lt;br /&gt;
&lt;br /&gt;
===Dezimal ausgeben===&lt;br /&gt;
Das Prinzip der Umwandlung ist einfach. Um herauszufinden wieviele Hunderter in der Zahl 123 enthalten sind, genügt es in einer Schleife immer wieder 100 von der Zahl abzuziehen und mitzuzählen wie oft dies gelang, bevor das Ergebnis negativ wurde. In diesem Fall lautet die Antwort: 1 mal, denn 123 - 100 macht 23. Versucht man erneut 100 anzuziehen, so ergibt sich eine negative Zahl.&lt;br /&gt;
Also muss eine &#039;1&#039; ausgeben werden. Die verbleibenden 23 werden weiterbehandelt, indem festgestellt wird wieviele Zehner darin enthalten sind. Auch hier wiederrum: In einer Schleife solange 10 abziehen, bis das Ergebnis nagativ wurde. Konkret geht das 2 mal gut, also muss das nächste auszugebende Zeichen ein &#039;2&#039; sein. Damit verbleiben noch die Einer, welche direkt in das entsprechende Zeichen umgewandelt werden können. In Summe hat man also an das Display die Zeichen &#039;1&#039; &#039;2&#039; &#039;3&#039; ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number:&lt;br /&gt;
           push  temp2            ; die Funktion verändert temp2, also sichern&lt;br /&gt;
                                  ; wir den Inhalt, um ihn am Ende wieder&lt;br /&gt;
                                  ; herstellen zu können&lt;br /&gt;
&lt;br /&gt;
           mov   temp2, temp1     ; das Register temp1 frei machen&lt;br /&gt;
                                  ; abzählen wieviele Hunderter&lt;br /&gt;
                                  ; in der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
           subi  temp2, 100       ; 100 abziehen&lt;br /&gt;
           brcs  lcd_number_2     ; ist dadurch ein Unterlauf entstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Hunderter mehr ...&lt;br /&gt;
           rjmp  lcd_number_1     ; ... und ab zur nächsten Runde&lt;br /&gt;
;&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorherhgehende Schleife 100 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; abzählen wieviele Zehner in&lt;br /&gt;
                  ; der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_3:&lt;br /&gt;
           subi  temp2, 10        ; 10 abziehen&lt;br /&gt;
           brcs  lcd_number_4     ; ist dadurch ein Unterlauf enstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Zehner mehr ...&lt;br /&gt;
           rjmp  lcd_number_3     ; ... und ab zur nächsten Runde&lt;br /&gt;
&lt;br /&gt;
                              ; die Zehnerstelle ausgeben&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; die übrig gebliebenen Einer&lt;br /&gt;
                  ; noch ausgeben&lt;br /&gt;
           ldi   temp1, &#039;0&#039;       ; die Zahl in temp2 ist jetzt im Bereich&lt;br /&gt;
           add   temp1, temp2     ; 0 bis 9. Einfach nur den ASCII Code für&lt;br /&gt;
           rcall lcd_data         ; &#039;0&#039; dazu addieren und wir erhalten dierekt&lt;br /&gt;
                                  ; den ASCII Code für die Ziffer&lt;br /&gt;
&lt;br /&gt;
           pop   temp2            ; den gesicherten Inhalt von temp2 wieder herstellen&lt;br /&gt;
           ret                    ; und zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beachte: Diese Funktion benutzt wiederrum die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039;. Anders als bei den bisherigen Aufrufen ist &#039;&#039;&#039;lcd_number&#039;&#039;&#039; aber darauf angewiesen, dass &#039;&#039;&#039;lcd_data&#039;&#039;&#039; das Register &#039;&#039;&#039;temp2&#039;&#039;&#039; unangetastet lässt. Falls sie es noch nicht getan haben, dann ist das jetzt die perfekte Gelegenheit, &#039;&#039;&#039;lcd_data&#039;&#039;&#039; mit den entsprechenden &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; Befehlen zu versehen. Sie sollten dies unbedingt zur Übung selbst machen. Am Ende muß die Funktion dann wie diese hier aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1           ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap  temp1                  ; Vertauschen&lt;br /&gt;
           andi  temp1, 0b00001111      ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr   temp1, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp1        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi  temp2, 0b00001111      ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr   temp2, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp2        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur ohne RS zu setzen&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           andi  temp1, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi  temp2, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz zur Funktionsweise der Funktion &#039;&#039;&#039;lcd_number&#039;&#039;&#039;: Die Zahl in einem Register bewegt sich im Wertebereich 0 bis 255. Um herauszufinden, wie die Hunderterstelle lautet, zieht die Funktion einfach in einer Schleife immer wieder 100 von der Schleife ab, bis bei der Subtraktion ein Unterlauf, angezeigt durch das Setzen des Carry-Bits bei der Subtraktion, entsteht. Die Anzahl wird im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; mitgezählt. Da dieses Register mit dem ASCII Code von &#039;0&#039; initialisiert wurde, und dieser ASCII Code bei jedem Schleifendurchlauf um 1 erhöht wird, können wir das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; direkt zur Ausgabe des Zeichens für die Hunderterstelle durch die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; benutzen. Völlig analog funktioniert auch die Ausgabe der Zehnerstelle.&lt;br /&gt;
&lt;br /&gt;
===Unterdrückung von führenden Nullen===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung: Diese Routine ist fehlerhaft&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt jede Zahl im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; immer mit 3 Stellen aus. Führende Nullen werden nicht unterdrückt. Möchte man dies ändern, so ist das ganz leicht möglich: Vor Ausgabe der Hunderterstelle bzw. Zehnerstelle muss lediglich überprüft werden, ob die Entsprechende Ausgabe eine &#039;0&#039; wäre. Ist sie das, so wird die Ausgabe übersprungen. Lediglich in der Einerstelle wird jede Ziffer wie errechnet ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ...&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben, wenn&lt;br /&gt;
                                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_2a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_2a:&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&lt;br /&gt;
           ...&lt;br /&gt;
                              ; die Zehnerstelle ausgeben, wenn&lt;br /&gt;
                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_4a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_4a:&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Verfahren, die einzelnen Stellen durch Subtraktion zu bestimmen, ist bei kleinen Zahlen eine durchaus gängige Alternative. Vor allem dann, wenn keine hardwaremäßige Unterstützung für Multiplikation und Division zur Verfügung steht. Ansonsten könnte man die die einzelnen Ziffern auch durch Division bestimmen. Das Prinzip ist folgendes (beispielhaft an der Zahl 52783 gezeigt)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   52783 / 10          -&amp;gt; 5278&lt;br /&gt;
   52783 - 5278 * 10   -&amp;gt;          3&lt;br /&gt;
&lt;br /&gt;
   5278 / 10           -&amp;gt; 527&lt;br /&gt;
   5278 - 527 * 10     -&amp;gt;          8&lt;br /&gt;
&lt;br /&gt;
   527 / 10            -&amp;gt; 52&lt;br /&gt;
   527 - 52 * 10       -&amp;gt;          7&lt;br /&gt;
&lt;br /&gt;
   52 / 10             -&amp;gt; 5&lt;br /&gt;
   52 - 5 * 10         -&amp;gt;          2&lt;br /&gt;
&lt;br /&gt;
   5 / 10              -&amp;gt; 0&lt;br /&gt;
   5 - 0 * 10          -&amp;gt;          5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Prinzip ist also die Restbildung bei einer fortgesetzten Division durch 10, wobei die einzelnen Ziffern in umgekehrter Reihenfolge ihrer Wertigkeit entstehen. Dadurch hat man aber ein Problem: Damit die Zeichen in der richtigen Reihenfolge ausgegeben werden können, muß man sie meistens zwischenspeichern um sie in der richtigen Reihenfole ausgeben zu können. Wird die Zahl in einem Feld von immer gleicher Größe ausgegeben, dann kann man auch die Zahl von rechts nach links ausgeben (bei einem LCD ist das möglich).&lt;br /&gt;
&lt;br /&gt;
===Hexadezimal ausgeben===&lt;br /&gt;
&lt;br /&gt;
Zu guter letzt hier noch eine Funktion, die eine Zahl aus dem Register &#039;&#039;&#039;temp1&#039;&#039;&#039; in hexadezimaler Form ausgibt. Die Funktion weist keine Besonderheiten auf und sollte unmittelbar verständlich sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen hexadezimal ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number_hex:&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           rcall lcd_number_hex_digit&lt;br /&gt;
           swap  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_number_hex_digit:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
           andi  temp1, $0F&lt;br /&gt;
           cpi   temp1, 10&lt;br /&gt;
           brlt  lcd_number_hex_digit_1&lt;br /&gt;
           subi  temp1, -( &#039;A&#039; - &#039;9&#039; - 1 ) ; es wird subi mit negativer Konstante verwendet, weil es kein addi gibt&lt;br /&gt;
lcd_number_hex_digit_1:&lt;br /&gt;
           subi  temp1, -&#039;0&#039;               ; ditto&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           &lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Eine 16-Bit Zahl aus einem Registerpärchen ausgeben===&lt;br /&gt;
&lt;br /&gt;
Um eine 16 Bit Zahl auszugeben wird wieder das bewährte Schema benutzt die einzelnen Stellen durch Subtraktion abzuzählen. Da es sich hierbei allerdings um eine 16 Bit Zahl handelt, müssen die Subtraktionen als 16-Bit Arithmetik ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 16 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp2 (low Byte) / temp3 (high Byte)&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number16:&lt;br /&gt;
           push  temp1&lt;br /&gt;
           push  temp2&lt;br /&gt;
           push  temp3&lt;br /&gt;
&lt;br /&gt;
; die Zehntausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number0:&lt;br /&gt;
           subi  temp2, low(10000)&lt;br /&gt;
           sbci  temp3, high(10000)&lt;br /&gt;
           brcs  lcd_number1&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number0&lt;br /&gt;
&lt;br /&gt;
; .. und ausgeben&lt;br /&gt;
lcd_number1:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-10000)&lt;br /&gt;
           sbci  temp3, high(-10000)&lt;br /&gt;
&lt;br /&gt;
; die Tausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number2:&lt;br /&gt;
           subi  temp2, low(1000)&lt;br /&gt;
           sbci  temp3, high(1000)&lt;br /&gt;
           brcs  lcd_number3&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number2&lt;br /&gt;
&lt;br /&gt;
; ... und ausgeben&lt;br /&gt;
lcd_number3:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-1000)&lt;br /&gt;
           sbci  temp3, high(-1000)&lt;br /&gt;
&lt;br /&gt;
; Als nächtes kommt die Hunderterstelle drann&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number4:&lt;br /&gt;
           subi  temp2, low(100)&lt;br /&gt;
           sbci  temp3, high(100)&lt;br /&gt;
           brcs  lcd_number5&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number4&lt;br /&gt;
&lt;br /&gt;
; und ausgeben&lt;br /&gt;
lcd_number5:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100&lt;br /&gt;
&lt;br /&gt;
; bleiben noch die Zehner&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number6:&lt;br /&gt;
           subi  temp2, 10&lt;br /&gt;
           brcs  lcd_number7&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number6&lt;br /&gt;
&lt;br /&gt;
; ausgeben ...&lt;br /&gt;
lcd_number7:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10&lt;br /&gt;
&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
           add   temp1, temp2&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
; fertig. Stack wieder aufräumen&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           pop   temp3&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Der überarbeitete, komplette Code==&lt;br /&gt;
&lt;br /&gt;
Hier also die komplett überarbeitete Version der LCD Funktionen.&lt;br /&gt;
&lt;br /&gt;
Die für die Benutzung relevanten Funktionen&lt;br /&gt;
* &#039;&#039;&#039;lcd_init&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_clear&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_home&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_data&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_flash_string&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number_hex&#039;&#039;&#039;&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; übergeben, wobei &#039;&#039;&#039;temp1&#039;&#039;&#039; vom Usercode definiert werden muss.&lt;br /&gt;
&lt;br /&gt;
[[Media:lcd-routines.asm|Download lcd-routines.asm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Stack|&lt;br /&gt;
zurücklink=AVR-Tutorial: Stack|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Interrupts|&lt;br /&gt;
vorlink=AVR-Tutorial: Interrupts}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister SREG) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register temp1 übergeben, wobei temp1 vom Usercode definiert werden muss. &lt;br /&gt;
&lt;br /&gt;
der progger hat vergessen das Z reg in den sub&#039;s lcd_flash_string ...&lt;br /&gt;
zu sichern, aber danke für den Einstig&lt;br /&gt;
&lt;br /&gt;
Lars&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27995</id>
		<title>AVR-Tutorial: LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27995"/>
		<updated>2008-05-16T03:02:01Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Initialisierung des Displays */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kaum ein elektronisches Gerät kommt heutzutage noch ohne ein LCD daher. Ist doch auch praktisch, Informationen im Klartext anzeigen zu können, ohne irgendwelche LEDs blinken zu lassen. Kein Wunder also, dass die häufigste Frage in Mikrocontroller-Foren ist: &amp;quot;Wie kann ich ein LCD anschließen?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-Bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-Bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-Bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-Bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-Bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-Bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4 Bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert. Ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitet hat. Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt, da wir wissen, welche Anschlüsse das LCD benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Pinnummer&amp;lt;BR&amp;gt;LCD || Bezeichnung || Anschluss&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |1 || Vss || GND&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |2 || Vcc || 5V&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |3 || Vee || GND oder [[Potentiometer | Poti]] &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |4 || RS || PD4 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |5 || RW || GND &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |6 || E || PD5 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |7 || DB0 || nicht angeschlossen &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |8 || DB1 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |9 || DB2 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |10 || DB3 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |11 || DB4 || PD0 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |12 || DB5 || PD1 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |13 || DB6 || PD2 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |14 || DB7 || PD3 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |15 || A || Vorsicht! Meistens nicht direkt an +5V anschließbar,&amp;lt;BR&amp;gt;nur über einen Vorwiderstand (z.B. 33&amp;amp;Omega;)!&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |16 || K || GND&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Ok alles ist verbunden. Wenn man jetzt den Strom einschaltet, sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display?&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung des LCDs im 4-Bit-Modus ==&lt;br /&gt;
&lt;br /&gt;
Um ein Byte zu übertragen, muss man es erstmal in die beiden Nibbles zerlegen, die getrennt übertragen werden. Da das obere Nibble (Bit 4 - Bit 7) als erstes übertragen wird, die 4 Datenleitungen jedoch an die vier unteren Bits des Port D angeschlossen sind, muss man die beiden Nibbles des zu übertragenden Bytes erstmal vertauschen. Der AVR kennt dazu praktischerweise einen eigenen Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           swap r16               ; vertauscht die beiden Nibbles von r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus 0b00100101 wird so z.B. 0b01010010. &lt;br /&gt;
&lt;br /&gt;
Jetzt sind die Bits für die erste Phase der Übertragung an der richtigen Stelle. Trotzdem wollen wir das Ergebnis nicht einfach so mit &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; an den Port geben. Um die Hälfte des Bytes, die jetzt nicht an die Datenleitungen des LCDs gegeben wird auf null zu setzen, verwendet man folgenden Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           andi r16, 0b00001111   ; Nur die vier unteren (mit 1 markierten)&lt;br /&gt;
                                  ; Bits werden übernommen, alle anderen werden null&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also: Das obere Nibble wird erst mit dem unteren vertauscht, damit es unten ist. Dann wird das obere (das wir jetzt noch nicht brauchen) auf null gesetzt. &lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem LCD noch mitteilen, ob wir Daten oder Befehle senden wollen. Das machen wir, indem wir das Bit, an dem RS angeschlossen ist (PD4), auf 0 (Befehl senden) oder auf 1 (Daten senden) setzen. Um ein Bit in einem normalen Register zu setzen, gibt es den Befehl sbr (Set Bit in Register). Dieser Befehl unterscheidet sich jedoch von sbi (das nur für IO-Register gilt) dadurch, dass man nicht die Nummer des zu setzenden Bits angibt, sondern eine Bitmaske. Das geht so: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbr r16, 0b00010000     ; Bit 4 setzen, alle anderen Bits bleiben gleich&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RS ist an PD4 angeschlossen. Wenn wir r16 an den Port D ausgeben, ist RS jetzt also high und das LCD erwartet Daten anstatt von Befehlen. &lt;br /&gt;
&lt;br /&gt;
Das Ergebnis können wir jetzt endlich direkt an den Port D übergeben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           out PORTD, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss vorher der Port D auf Ausgang geschalten werden, indem man 0xFF ins Datenrichtungsregister DDRD schreibt. &lt;br /&gt;
&lt;br /&gt;
Um dem LCD zu signalisieren, dass es das an den Datenleitungen anliegende Nibble übernehmen kann, wird die E-Leitung (Enable, an PD5 angeschlossen) auf high und kurz darauf wieder auf low gesetzt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbi PORTD, 5              ; Enable high&lt;br /&gt;
           nop                       ; 3 Taktzyklen warten (&amp;quot;nop&amp;quot; = nichts tun)&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5              ; Enable wieder low&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die eine Hälfte des Bytes wäre damit geschafft! Die andere Hälfte kommt direkt hinterher: Alles, was an der obenstehenden Vorgehensweise geändert werden muss, ist, das &amp;quot;swap&amp;quot; (Vertauschen der beiden Nibbles) wegzulassen.&lt;br /&gt;
&lt;br /&gt;
== Initialisierung des Displays ==&lt;br /&gt;
&lt;br /&gt;
Allerdings gibt es noch ein Problem. Wenn ein LCD eingeschaltet wird, dann läuft es zunächst im 8 Bit Modus. Irgendwie muss das Display initialisiert und auf den 4 Bit Modus umgeschaltet werden, und zwar nur mit den 4 zur Verfügung stehenden Datenleitungen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn es Probleme gibt, dann meistens an diesem Punkt. Die &amp;quot;kompatiblen&amp;quot; Kontroller sind gelegentlich doch nicht 100% identisch. Es lohnt sich, das Datenblatt (siehe Weblinks im Artikel [[LCD]]) genau zu lesen, in welcher Reihenfolge und mit welchen Abständen (Delays) die Initialisierungbefehle gesendet werden. Eine weitere Hilfe können Ansteuerungsbeispiele in Forenbeiträgen geben z.B.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79609#664268 (A) KS0066U oder Ähnliche --- LCD Treiber]&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 4 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Achtung: Im Folgenden sind alle Bytes aus Sicht des LCD-Kontrollers angegeben! Da LCD-seitig nur die Leitungen DB4 - DB7 verwendet werden, ist daher immer nur das höherwertige Nibble gültig. Durch die Art der Verschaltung (DB4 - DB7 wurde auf dem PORT an PD0 bis PD3 angeschlossen) ergibt sich eine Verschiebung, so dass das am Kontroller auszugebende Byte nibblemässig vertauscht ist!&lt;br /&gt;
&lt;br /&gt;
Die Sequenz, aus Sicht des Kontrollers, sieht so aus:&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* $2 ins Steuerregister schreiben (RS = 0), dadurch wird auf 4 Bit Daten umstellt&lt;br /&gt;
* Ab jetzt muss für die Übertragung eines Bytes jeweils zuerst das höherwertige Nibble und dann das niederwertige Nibble übertragen werden, wie oben beschrieben&lt;br /&gt;
* Mit dem Konfigurier-Befehl $20 das Display konfigurieren (4-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 8 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Der Vollständigkeit halber hier noch die notwendige Initialiserungssequenz für den 8 Bit Modus. Da hier die Daten komplett als 1 Byte übertragen werden können, sind einige Klimmzüge wie im 4 Bit Modus nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
* Nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 4.1ms warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mindestens 100µs warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mit dem Konfigurier-Befehl 0x30 das Display konfigurieren (8-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* Mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
== Routinen zur LCD-Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
Die Routinen zur Kommunikation mit dem LCD sehen also so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000 (Anm.1)&lt;br /&gt;
           out PORTD, temp1             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000&lt;br /&gt;
           out PORTD, temp2             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out PORTD, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
 ;&lt;br /&gt;
 ; Bei höherem Takt (&amp;gt;= 8 MHz) kann es notwendig sein, &lt;br /&gt;
 ; vor dem Enable High 1-2 Wartetakte (nop) einzufügen. &lt;br /&gt;
 ; Siehe dazu http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi PORTD, 5                 ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5                 ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
&lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi  temp3,50&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall  delay5ms&lt;br /&gt;
           dec  temp3&lt;br /&gt;
           brne powerupwait&lt;br /&gt;
           ldi temp1, 0b00000011        ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out PORTD, temp1             ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00000010        ; 4bit-Modus einstellen&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00101000        ; 4Bit / 2 Zeilen / 5x8&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00001100        ; Display ein / Cursor aus / kein Blinken&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00000100        ; inkrement / kein Scrollen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi temp1, 0b00000001   ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi temp1, 0b00000010   ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anm.1: Siehe [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
Weitere Funktionen (wie z.B. Cursorposition verändern) sollten mit Hilfe der [[AVR-Tutorial:_LCD#Welche_Befehle_versteht_das_LCD.3F|Befehlscodeliste]] nicht schwer zu realisieren sein. Einfach den Code in temp laden, lcd_command aufrufen und ggf. eine Pause einfügen. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man die LCD-Ansteuerung auch an einen anderen Port des Mikrocontrollers &amp;quot;verschieben&amp;quot;: Wenn das LCD z.B. an Port B angeschlossen ist, dann reicht es im Programm alle &amp;quot;PORTD&amp;quot; durch &amp;quot;PORTB&amp;quot; und &amp;quot;DDRD&amp;quot; durch &amp;quot;DDRB&amp;quot; zu ersetzen. &lt;br /&gt;
&lt;br /&gt;
Wer eine höhere Taktfrequenz als 4 MHz verwendet, der sollte daran denken die Dauer der Verzögerungsschleifen anzupassen.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das diese Routinen zur Anzeige von Text verwendet, kann z.B. so aussehen (die Datei lcd-routines.asm muss sich im gleichen Verzeichnis befinden). Nach der Initialisierung wird zuerst der Displayinhalt gelöscht. Um dem LCD ein Zeichen zu schicken, lädt man es in temp1 und ruft die Routine &amp;quot;lcd_data&amp;quot; auf. Das folgende Beispiel zeigt das Wort &amp;quot;Test&amp;quot; auf dem LCD an. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/lcd-test.asm Download lcd-test.asm] &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;
&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, 0xFF    ; Port D = Ausgang&lt;br /&gt;
           out DDRD, temp1&lt;br /&gt;
&lt;br /&gt;
           rcall lcd_init     ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear    ; Display löschen&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;T&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;e&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           &lt;br /&gt;
           ldi temp1, &#039;s&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;t&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD-Routinen werden hier eingefügt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für längere Texte ist die Methode, jedes Zeichen einzeln in das Register zu laden und &amp;quot;lcd_data&amp;quot; aufzurufen natürlich nicht sehr praktisch. Dazu später aber mehr.&lt;br /&gt;
&lt;br /&gt;
Bisher wurden in Register immer irgendwelche Zahlenwerte geladen, aber in diesem Programm kommt plötzlich die Anweisung&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
vor. Wie ist diese zu verstehen? Passiert hier etwas grundlegend anderes als beim Laden einer Zahl in ein Register?&lt;br /&gt;
&lt;br /&gt;
Die Antwort darauf lautet: Nein. Auch hier wird letztendlich nur eine Zahl in ein Register geladen. Der Schlüssel zum Verständnis beruht darauf, dass zum LCD, so wie zu allen Ausgabegeräten, für die Ausgabe von Texten immer nur Zahlen übertragen werden, sog. Codes. Zum Beispiel könnte man vereinbaren, dass ein LCD, wenn es den Ausgabecode 65 erhält ein &#039;A&#039; anzeigt, bei einem Ausgabecode von 66 ein &#039;B&#039; usw. Naturgemäß gibt es daher viele verschiedene Code-Buchstaben Zuordnungen. Damit hier etwas Ordnung in das potentielle Chaos kommt, hat man sich bereits in der Steinzeit der Programmierung auf bestimmte Codetabellen geeinigt, von denen die verbreitetste sicherlich die ASCII-Zuordnung ist.&lt;br /&gt;
&lt;br /&gt;
==ASCII==&lt;br /&gt;
&lt;br /&gt;
ASCII steht für &#039;&#039;American Standard Code for Information Interchange&#039;&#039; und ist ein standardisierter Code zu Zeichenumsetzung. Die Codetabelle sieht hexadezimal dabei wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;.0&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x1&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x2&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x3&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x4&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x6&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x7&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x8&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x9&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xA&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xB&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xC&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xD&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xE&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xF&amp;lt;/th&amp;gt;&amp;lt;br&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;&#039;&#039;&#039;0x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NUL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SOH&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;STX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EOT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ENQ&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ACK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BEL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;HT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;VT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CR&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SO&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SI&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;&#039;&#039;&#039;1x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DLE&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NAK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SYN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CAN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EM&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SUB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ESC&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;US&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;&#039;&#039;&#039;2x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SP&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;!&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;#&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;%&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;amp;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;(&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;)&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;*&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;+&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;,&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;.&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;3x&#039;&#039;&#039;&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;&amp;lt;td&amp;gt;3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;9&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;=&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;gt;&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;4x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;B&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;C&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;D&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;F&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;G&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;H&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;I&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;J&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;L&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;M&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;N&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;O&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;&#039;&#039;&#039;5x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;P&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;R&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;S&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;T&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;U&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;V&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;W&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;X&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;[&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;\&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;]&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;^&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;6x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;`&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;a&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;b&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;c&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;d&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;e&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;f&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;g&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;h&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;i&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;j&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;k&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;l&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;m&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;n&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;o&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;&#039;&#039;&#039;7x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;p&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;r&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;s&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;t&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;u&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;v&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;w&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;{&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;|&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;~&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DEL&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 ersten beiden Zeilen enthalten die Codes für einige Steuerzeichen, ihre vollständige Beschreibung würde hier zu weit führen. Das Zeichen &#039;&#039;&#039;SP&#039;&#039;&#039; steht für ein &#039;&#039;Space&#039;&#039;, also ein Leerzeichen. &#039;&#039;&#039;BS&#039;&#039;&#039; steht für &#039;&#039;Backspace&#039;&#039;, also ein Zeichen zurück. &#039;&#039;&#039;DEL&#039;&#039;&#039; steht für &#039;&#039;Delete&#039;&#039;, also das Löschen eines Zeichens. &#039;&#039;&#039;CR&#039;&#039;&#039; steht für &#039;&#039;Carriage Return&#039;&#039;, also wörtlich: der Wagenrücklauf (einer Schreibmaschine), während &#039;&#039;&#039;LF&#039;&#039;&#039; für &#039;&#039;Line feed&#039;&#039;, also einen Zeilenvorschub steht.&lt;br /&gt;
&lt;br /&gt;
Der Assembler kennt diese Codetabelle und ersetzt die Zeile&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, $54&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
was letztendlich auch der Lesbarkeit des Programmes zugute kommt. Funktional besteht kein Unterschied zwischen den beiden Anweisungen. Beide bewirken, dass das Register temp1 mit dem Bitmuster 01010100 ( = hexadezimal 54, = dezimal 84 oder eben der ASCII Code für &#039;&#039;&#039;T&#039;&#039;&#039;) geladen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das LCD wiederrum kennt diese Code-Tabelle ebenfalls und wenn es über den Datenbus die Codezahl $54 zur Anzeige empfängt, dann schreibt es ein &#039;&#039;&#039;T&#039;&#039;&#039; an die aktuelle Cursorposition. Genauer gesagt, weiss das LCD nichts von einem &#039;&#039;&#039;T&#039;&#039;&#039;. Es sieht einfach in seinen internen Tabellen nach, welche Pixel beim Empfang der Codezahl $54 auf schwarz zu setzen sind. &#039;Zufällig&#039; sind das genau jene Pixel, die für uns Menschen ein &#039;&#039;&#039;T&#039;&#039;&#039; ergeben.&lt;br /&gt;
&lt;br /&gt;
==Welche Befehle versteht das LCD?==&lt;br /&gt;
&lt;br /&gt;
Auf dem LCD arbeitet ein Kontroller vom Typ HD44780. Diesen Kontroller versteht eine Reihe von Befehlen, die allesamt mittels lcd_command gesendet werden können. Ein Kommando ist dabei nichts anderes als ein Befehlsbyte, indem die verschiedenen Bits verschiedene Bedeutung besitzen:&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 0 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 1 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;der Zustand dieses Bits ist egal&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;sonstige Buchstaben&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;das Bit muss je nach gewünschter Funktionalität gesetzt werden. Die mögliche Funktionalität des jeweiligen Bits geht aus der Befehlsbeschreibung hervor&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Kommando &#039;ON/OFF Control&#039; soll benutzt werden um das Display einzuschalten, der Cursor soll eingeschaltet werden und der Cursor soll blinken.&lt;br /&gt;
Das Befehlsbyte ist so aufgebaut:&lt;br /&gt;
   0b00001dcb&lt;br /&gt;
Aus der Befehlsbeschreibung entnimmt man:&lt;br /&gt;
* Display ein bedeutet, dass an der Bitposition d eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor ein bedeutet, dass an der Bitposition c ein 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor blinken bedeutet, dass an der Bitposition b eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
Das dafür zu übertragende Befehlsbyte hat also die Gestalt 0b00001111 oder in hexadezimaler Schreibweise $0F&lt;br /&gt;
&lt;br /&gt;
===Clear display: 0b00000001===&lt;br /&gt;
&lt;br /&gt;
Die Anzeige wird gelöscht und der Ausgabecursor kehrt an die Home Position (links, erste Zeile) zurück&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Cursor home: 0b0000001x===&lt;br /&gt;
&lt;br /&gt;
Der Cursor kehrt an die Home Position (links, erste Zeile) zurück. Ein verschobenes Display wird auf die Grundeinstellung zurückgesetzt.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs bis 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Entry mode: 0b000001is===&lt;br /&gt;
&lt;br /&gt;
Legt die Cursor Richtung fest sowie eine mögliche Verschiebung des Displays fest&lt;br /&gt;
* i = 1, Cursorposition bei Ausgabe eines Zeichens erhöhen&lt;br /&gt;
* i = 0, Cursorposition bei Ausgabe eines Zeichens vermindern&lt;br /&gt;
* s = 1, Display wird gescrollt, wenn der Cursor das Ende/Anfang, je nach Einstellung von i, erreicht hat.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===On/off control: 0b00001dcb===&lt;br /&gt;
&lt;br /&gt;
Display insgesamt ein/ausschalten; den Cursor ein/ausschalten; den Cursor auf blinken schalten/blinken aus. Wenn das Display ausgeschaltet wird, geht der Inhalt des Displays nicht verloren. Der vorher angezeigte Text wird nach wiedereinschalten erneut angezeigt.&lt;br /&gt;
Ist der Cursor eingeschaltet, aber Blinken ausgeschaltet, so wird der Cursor als Cursorzeile in Pixelzeile 8 dargestellt. Ist Blinken eingeschaltet, wird der Cursor als blinkendes ausgefülltes Rechteck dargestellt, welches abwechselnd mit dem Buchstaben an dieser Stelle angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
* d = 0, Display aus&lt;br /&gt;
* d = 1, Display ein&lt;br /&gt;
* c = 0, Cursor aus&lt;br /&gt;
* c = 1, Cursor ein&lt;br /&gt;
* b = 0, Cursor blinken aus&lt;br /&gt;
* b = 1, Cursor blinken ein&lt;br /&gt;
 &lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Cursor/Scrollen: 0b0001srxx===&lt;br /&gt;
&lt;br /&gt;
Bewegt den Cursor oder scrollt das Display um eine Position entweder nach rechts oder nach links.&lt;br /&gt;
&lt;br /&gt;
* s = 1, Display scrollen&lt;br /&gt;
* s = 0, Cursor bewegen&lt;br /&gt;
* r = 1, nach rechts&lt;br /&gt;
* r = 0, nach links &lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Konfiguration: 0b001dnfxx===&lt;br /&gt;
&lt;br /&gt;
Einstellen der Interface Art, Modus, Font&lt;br /&gt;
* d = 0, 4-Bit Interface&lt;br /&gt;
* d = 1, 8-Bit Interface&lt;br /&gt;
* n = 0, 1 zeilig&lt;br /&gt;
* n = 1, 2 zeilig&lt;br /&gt;
* f = 0, 5x7 Pixel&lt;br /&gt;
* f = 1, 5x11 Pixel&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Character RAM Address Set: 0b01aaaaaa===&lt;br /&gt;
&lt;br /&gt;
Mit diesem Kommando werden maximal 8 selbst definierte Zeichen definiert. Dazu wird der Character RAM Zeiger auf den Anfang des Character Generator (CG) RAM gesetzt und das Zeichen durch die Ausgabe von 8 Byte definiert. Der Adress Zeiger wird nach Ausgabe jeder Pixelzeile (8Bit) vom LCD selbst erhöht. Nach Beendigung der Zeichendefinition muss die Schreibposition explizit mit dem Kommando &amp;quot;Display RAM Address Set&amp;quot; wieder in den DD-RAM Bereich gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
aaaaaa 6-bit CG RAM Adresse&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Display RAM Address Set: 0b1aaaaaaa===&lt;br /&gt;
&lt;br /&gt;
Den Cursor neu positionieren. Display Data (DD) Ram ist vom Character Generator (CG) Ram unabhängig. Der Adresszeiger wird bei Ausgabe eines Zeichens ins DD Ram automatisch erhöht. Das Display verhält sich so, als ob eine Zeile immer aus 32 logischen Zeichen besteht, von der, je nach konkretem Displaytyp (16 Zeichen, 20 Zeichen) immer nur ein Teil sichtbar ist.&lt;br /&gt;
&lt;br /&gt;
aaaaaaa 7-bit DD RAM Adresse. Auf 2-zeiligen Displays (und den meisten 16x1 Displays), kann die Adressangabe wie folgt interpretiert werden&lt;br /&gt;
&lt;br /&gt;
1laaaaaa&lt;br /&gt;
* l = Zeilennummer (0 oder 1)&lt;br /&gt;
* a = 6-Bit Spaltennummer&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
==Einschub: Code aufräumen==&lt;br /&gt;
&lt;br /&gt;
Es wird Zeit sich einmal etwas kritisch mit den bisher geschriebenen Funktionen auseinander zu setzen.&lt;br /&gt;
&lt;br /&gt;
===Portnamen aus dem Code herausziehen===&lt;br /&gt;
&lt;br /&gt;
Wenn wir die LCD-Funktionen einmal genauer betrachten, dann fällt sofort auf, daß über die Funktionen verstreut immer wieder das &#039;&#039;&#039;PORTD&#039;&#039;&#039; sowie einzelne Zahlen für die Pins an diesem Port auftauchen. Wenn das LCD an einem anderen Port betrieben werden soll, oder sich die Pin-Belegung ändert, dann muß an all diesen Stellen eine Anpassung vorgenommen werden. Dabei darf keine einzige Stelle übersehen werden, ansonsten würden die LCD-Funktionen nicht oder nicht vollständig funktionieren.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit dem vorzubeugen ist es, diese immer gleichbleibenden Dinge an den Anfang der LCD-Funktionen vorzuziehen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
.equ PIN_E    = 5&lt;br /&gt;
.equ PIN_RS   = 4&lt;br /&gt;
&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp1          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp2          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi LCD_PORT, PIN_E          ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi LCD_PORT, PIN_E          ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
 &lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf Ausgang&lt;br /&gt;
           out   LCD_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi   temp3,6&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           dec   temp3&lt;br /&gt;
           brne  powerupwait&lt;br /&gt;
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out   LCD_PORT, temp1        ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00101000      ; 4 Bot, 2 Zeilen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00001100      ; Display on, Cursor off&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00000100      ; endlich fertig&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi   temp1, 0b00000010      ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels &#039;&#039;&#039;.equ&#039;&#039;&#039; werden mit dem Assembler Textersetzungen vereinbart. Der Assembler ersetzt alle Vorkomnisse des Quelltextes durch den zu ersetzenden Text. Dadurch ist es zb. möglich alle Vorkommnisse von &#039;&#039;&#039;PORTD&#039;&#039;&#039; durch &#039;&#039;&#039;LCD_PORT&#039;&#039;&#039; auszutauschen. Wird das LCD an einen anderen Port, zb. &#039;&#039;&#039;PORTB&#039;&#039;&#039; gelegt, dann genügt es, einzig und alleine die Zeilen&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTB&lt;br /&gt;
.equ LCD_DDR  = DDRB&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu ersetzen. Der Assembler sorgt dann dafür, dass diese Portänderung an den relevanten Stellen im Code über die Textersetzungen einfliesst. Selbiges natürlich mit der Pin-Zuordnung.&lt;br /&gt;
&lt;br /&gt;
===Registerbenutzung===&lt;br /&gt;
&lt;br /&gt;
Bei diesen Funktionen mussten einige Register des Prozessors benutzt werden um darin Zwischenergebnisse zu speichern bzw. zu bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss dabei natürlich, dass es zu keinen Überschneidungen kommt. Solange nur jede Funktion jeweils für sich betrachtet wird, ist das kein Problem. In 20 oder 30 Code-Zeilen kann man gut verfolgen, welches Register wofür benutzt wird. Schwieriger wird es, wenn Funktionen wiederrum andere Funktionen aufrufen, die ihrerseits wieder Funktionen aufrufen usw. Jede dieser Funktionen benutzt einige Register und mit zunehmender Programmgröße wird es immer schwieriger zu verfolgen, welches Register zu welchem Zeitpunkt wofür benutzt wird.&lt;br /&gt;
&lt;br /&gt;
Speziell bei Basisfunktionen, wie diesen LCD-Funktionen, ist es daher oft ratsam, dafür zu sorgen, daß jede Funktion die Register wieder in dem Zustand hinterlässt, indem sie sie auch vorgefunden hat. Wir benötigen dazu wieder den Stack, auf dem die Registerinhalte bei Betreten einer Funktion zwischengespeichert werden und von dem die Register bei Verlassen einer Funktion wiederhergestellt werden.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir die Funktion&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion verändert das Register temp1. Um das Register abzusichern schreiben wir die Funktion um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           push  temp1                  ; temp1 auf dem Stack sichern&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           pop   temp1                  ; temp1 vom Stack wiederherstellen&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am besten hält man sich an die Regel: Jede Funktion ist dafür zuständig die Register zu sichern und wiederherzustellen, die sie auch selbst verändert. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; ruft die Funktionen &#039;&#039;&#039;lcd_command&#039;&#039;&#039; und &#039;&#039;&#039;delay5ms&#039;&#039;&#039; auf. Wenn diese Funktionen selbst wieder Register verändern (und das tun sie), so ist es die Aufgabe dieser Funktionen, sich um die Sicherung und das Wiederherstellen der entsprechenden Register zu kümmern. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; sollte sich nicht darum kümmern müssen. Auf diese Weise ist das Schlimmste, das einem passieren kann, das ein paar Register unnütz gesichert und wiederhergestellt werden. Das kostet zwar etwas Rechenzeit und etwas Speicherplatz auf dem Stack, ist aber immer noch besser als das andere Extrem: Nach einem Funktionsaufruf haben einige Register nicht mehr den Wert den sie haben sollten und das Programm rechnet mit falschen Zahlen weiter.&lt;br /&gt;
&lt;br /&gt;
===Lass den Assembler rechnen===&lt;br /&gt;
Betrachtet man den Code genauer, so fallen einige konstante Zahlenwerte auf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Das vorangestellte $ kennzeichnet die Zahl als Hexadezimalzahl) Der Code benötigt eine Warteschleife die mindestens 50µs dauert. Die beiden Befehle innerhalb der Schleife benötigen 3 Takte: 1 Takt für den &#039;&#039;&#039;dec&#039;&#039;&#039; und der &#039;&#039;&#039;brne&#039;&#039;&#039; benötigt 2 Takte wenn die Bedingung zutrifft, der Branch also genommen wird. Bei 4 Mhz werden also 4000000 / 3 * 50 / 1000000 = 66.6 Durchläufe durch die Schleife benötigt um eine Verzögerungszeit von 50µs (0.000050 Sekunden) zu erreichen, hexadezimal ausgedrückt: $42&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist: Bei anderen Taktfrequenzen müsste man nun jedesmal diese Berechnung machen und den entsprechenden Zahlenwert einsetzen. Das kann aber der Assembler genausogut erledigen. Am Anfang des Codes wird ein Eintrag definiert, der die Taktfrequenz festlegt. Traditionell heist dieser Eintrag &amp;lt;i&amp;gt;XTAL&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ XTAL  = 4000000&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 50 / 3 ) / 1000000&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An einer anderen Codestelle gibt es weitere derartige magische Zahlen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was geht hier vor?&lt;br /&gt;
Die innere Schleife benötigt wieder 3 Takte pro Durchlauf. Bei $C9 = 201 Durchläufen werden also 201 * 3 = 603 Takte verbraucht. In der äußeren Schleife kommen pro Durchlauf alo 1 + 603 + 1 + 2 = 607 Takte verbraucht. Da die äußere Schleife $21 = 33 mal wiederholt wird, werden 20031 Takte verbraucht. Bei 4Mhz benötigt der Prozessor 20031 / 4000000 = 0.005007 Sekunden, also 5 ms.&lt;br /&gt;
Wird der Wiederholwert für die innere Schleife bei $C9 belassen, so werden 4000000 / 607 * 5 / 1000 Wiederholungen der äusseren Schleife benötigt. Auch diese Berechnung kann wieder der Assembler übernehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 5 / 607 ) / 1000&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein kleines Problem kann bei der Verwendung dieses Verfahrens entstehen: Bei hohen Taktfrequenzen und großen Wartezeiten kann der berechnete Wert größer als 255 werden und man bekommt die Fehlermeldung &amp;quot;Operand(s) out of range&amp;quot; beim Assemblieren. Dieser Fall tritt zum Beispiel für obige Konstruktion bei einer Taktfrequenz von 16 MHz ein (genauer gesagt ab 15,3 MHz), während darunter XTAL beliebig geändert werden kann. Als einfachste Lösung bietet es sich an, die Zahl der Takte pro Schleifendurchlauf durch das Einfügen von &#039;&#039;&#039;nop&#039;&#039;&#039; zu erhöhen und die Berechnungsvorschrift anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Ausgabe eines konstanten Textes ==&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde schon einmal ein Text ausgegeben. Dies geschah durch Ausgabe von einzelnen Zeichen. Das können wir auch anders machen. Wir können den Text im Speicher ablegen und eine Funktion schreiben, die die einzelnen Zeichen aus dem Speicher holt und ausgibt. Dabei erhebt sich aber eine Fragestellung: Woher weiß die Funktion eigentlich, wie lange der Text ist? Die Antwort darauf lautet: Sie kann es nicht wissen. Wir müssen irgendwelche Vereinbarungen treffen, woran die Funktion erkennen kann, dass der Text zu Ende ist. Im Wesentlichen werden dazu 2 Methoden benutzt:&lt;br /&gt;
* Der Text enthält ein spezielles Zeichen, welches das Ende des Textes markiert&lt;br /&gt;
* Wir speichern nicht nur den Text selbst, sondern auch die Länge des Textes&lt;br /&gt;
Mit einer der beiden Methoden ist es der Textausgabefunktion dann ein Leichtes, den Text vollständig auszugeben.&lt;br /&gt;
&lt;br /&gt;
Wir werden uns im Weiteren dafür entscheiden, ein spezielles Zeichen, eine 0, dafür zu benutzen. Die Ausgabefunktionen werden dann etwas einfacher, als wenn bei der Ausgabe die Anzahl der bereits ausgegebenen Zeichen mitgezählt werden muss.&lt;br /&gt;
&lt;br /&gt;
Den Text selbst speichern wir im Flash-Speicher, also dort wo auch das Programm gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Einen konstanten Text aus dem Flash Speicher&lt;br /&gt;
 ; ausgeben. Der Text wird mit einer 0 beendet&lt;br /&gt;
lcd_flash_string:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_1:&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           rjmp  lcd_flash_string_1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_2:&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion benutzt den Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039; um das jeweils nächste Zeichen aus dem Flash Speicher in ein Register zur Weiterverarbeitung zu laden. Dazu wird der sog. &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; benutzt. So nennt man das Registerpaar &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;. Nach jedem Ladevorgang wird dabei durch den Befehl&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
dieser Z-Pointer um 1 erhöht. Mittels &#039;&#039;&#039;cpi&#039;&#039;&#039; wird das in das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; geladene Zeichen mit 0 verglichen. &#039;&#039;&#039;cpi&#039;&#039;&#039; vergleicht die beiden Zahlen und merkt sich das Ergebnis in einem speziellen Register in Form von Status Bits. &#039;&#039;&#039;cpi&#039;&#039;&#039; zieht dabei ganz einfach die beiden Zahlen voneinander ab. Sind sie gleich, so kommt da als Ergebnis 0 heraus und &#039;&#039;&#039;cpi&#039;&#039;&#039; setzt daher konsequenter Weise das Zero-Flag, das anzeigt, daß die vorhergegangene Operation ein 0 Ergebnis hatte.&#039;&#039;&#039;breq&#039;&#039;&#039; wertet diese Status-Bits aus. Wenn die vorhergegangene Operation ein 0-Ergebnis hatte, das Zero-Flag also gesetzt ist, dann wird ein Sprung zum angegebenen Label durchgeführt. In Summe bewirkt also die Sequenz&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
das das gelesene Zeichen mit 0 verglichen wird und falls das gelesene&lt;br /&gt;
Zeichen tatsächlich 0 war, an der Stelle lcd_flash_string_2 weiter gemacht wird. Im anderen Fall wird die bereits geschriebene Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aufgerufen, welche das Zeichen ausgibt. &#039;&#039;&#039;lcd_data&#039;&#039;&#039; erwartet dabei das Zeichen im Register &#039;&#039;&#039;temp1&#039;&#039;&#039;, genau in dem Register in welches wir vorher mittels &#039;&#039;&#039;lpm&#039;&#039;&#039; das Zeichen geladen hatten.&lt;br /&gt;
&lt;br /&gt;
Das verwendende Programm sieht dann 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 temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
           rcall lcd_init              ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear             ; Display löschen&lt;br /&gt;
 &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;
&lt;br /&gt;
           rcall lcd_flash_string      ; Unterprogramm gibt String aus der&lt;br /&gt;
                                       ; durch den Z-Pointer adressiert wird&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
           .db &amp;quot;Test&amp;quot;,0                ; Stringkonstante, durch eine 0&lt;br /&gt;
                                       ; abgeschlossen  &lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD Funktionen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Genaueres über die Verwendung unterschiedlicher Speicher findet sich im Kapitel [[AVR-Tutorial:_Speicher|Speicher]]&lt;br /&gt;
&lt;br /&gt;
==Zahlen ausgeben==&lt;br /&gt;
Um Zahlen, die beispielsweise in einem Register gespeichert sind, ausgeben zu können, ist es notwendig sich eine Textrepräsentierung der Zahl zu generieren. Die Zahl 123 wird also in den Text &amp;quot;123&amp;quot; umgewandelt welcher dann ausgegeben wird. Aus praktischen Gründen wird allerdings der Text nicht vollständig generiert (man müsste ihn ja irgendwo zwischenspeichern) sondern die einzelnen Buchstaben werden sofort ausgegeben, sobald sie bekannt sind.&lt;br /&gt;
&lt;br /&gt;
===Dezimal ausgeben===&lt;br /&gt;
Das Prinzip der Umwandlung ist einfach. Um herauszufinden wieviele Hunderter in der Zahl 123 enthalten sind, genügt es in einer Schleife immer wieder 100 von der Zahl abzuziehen und mitzuzählen wie oft dies gelang, bevor das Ergebnis negativ wurde. In diesem Fall lautet die Antwort: 1 mal, denn 123 - 100 macht 23. Versucht man erneut 100 anzuziehen, so ergibt sich eine negative Zahl.&lt;br /&gt;
Also muss eine &#039;1&#039; ausgeben werden. Die verbleibenden 23 werden weiterbehandelt, indem festgestellt wird wieviele Zehner darin enthalten sind. Auch hier wiederrum: In einer Schleife solange 10 abziehen, bis das Ergebnis nagativ wurde. Konkret geht das 2 mal gut, also muss das nächste auszugebende Zeichen ein &#039;2&#039; sein. Damit verbleiben noch die Einer, welche direkt in das entsprechende Zeichen umgewandelt werden können. In Summe hat man also an das Display die Zeichen &#039;1&#039; &#039;2&#039; &#039;3&#039; ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number:&lt;br /&gt;
           push  temp2            ; die Funktion verändert temp2, also sichern&lt;br /&gt;
                                  ; wir den Inhalt, um ihn am Ende wieder&lt;br /&gt;
                                  ; herstellen zu können&lt;br /&gt;
&lt;br /&gt;
           mov   temp2, temp1     ; das Register temp1 frei machen&lt;br /&gt;
                                  ; abzählen wieviele Hunderter&lt;br /&gt;
                                  ; in der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
           subi  temp2, 100       ; 100 abziehen&lt;br /&gt;
           brcs  lcd_number_2     ; ist dadurch ein Unterlauf entstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Hunderter mehr ...&lt;br /&gt;
           rjmp  lcd_number_1     ; ... und ab zur nächsten Runde&lt;br /&gt;
;&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorherhgehende Schleife 100 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; abzählen wieviele Zehner in&lt;br /&gt;
                  ; der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_3:&lt;br /&gt;
           subi  temp2, 10        ; 10 abziehen&lt;br /&gt;
           brcs  lcd_number_4     ; ist dadurch ein Unterlauf enstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Zehner mehr ...&lt;br /&gt;
           rjmp  lcd_number_3     ; ... und ab zur nächsten Runde&lt;br /&gt;
&lt;br /&gt;
                              ; die Zehnerstelle ausgeben&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; die übrig gebliebenen Einer&lt;br /&gt;
                  ; noch ausgeben&lt;br /&gt;
           ldi   temp1, &#039;0&#039;       ; die Zahl in temp2 ist jetzt im Bereich&lt;br /&gt;
           add   temp1, temp2     ; 0 bis 9. Einfach nur den ASCII Code für&lt;br /&gt;
           rcall lcd_data         ; &#039;0&#039; dazu addieren und wir erhalten dierekt&lt;br /&gt;
                                  ; den ASCII Code für die Ziffer&lt;br /&gt;
&lt;br /&gt;
           pop   temp2            ; den gesicherten Inhalt von temp2 wieder herstellen&lt;br /&gt;
           ret                    ; und zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beachte: Diese Funktion benutzt wiederrum die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039;. Anders als bei den bisherigen Aufrufen ist &#039;&#039;&#039;lcd_number&#039;&#039;&#039; aber darauf angewiesen, dass &#039;&#039;&#039;lcd_data&#039;&#039;&#039; das Register &#039;&#039;&#039;temp2&#039;&#039;&#039; unangetastet lässt. Falls sie es noch nicht getan haben, dann ist das jetzt die perfekte Gelegenheit, &#039;&#039;&#039;lcd_data&#039;&#039;&#039; mit den entsprechenden &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; Befehlen zu versehen. Sie sollten dies unbedingt zur Übung selbst machen. Am Ende muß die Funktion dann wie diese hier aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1           ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap  temp1                  ; Vertauschen&lt;br /&gt;
           andi  temp1, 0b00001111      ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr   temp1, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp1        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi  temp2, 0b00001111      ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr   temp2, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp2        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur ohne RS zu setzen&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           andi  temp1, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi  temp2, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz zur Funktionsweise der Funktion &#039;&#039;&#039;lcd_number&#039;&#039;&#039;: Die Zahl in einem Register bewegt sich im Wertebereich 0 bis 255. Um herauszufinden, wie die Hunderterstelle lautet, zieht die Funktion einfach in einer Schleife immer wieder 100 von der Schleife ab, bis bei der Subtraktion ein Unterlauf, angezeigt durch das Setzen des Carry-Bits bei der Subtraktion, entsteht. Die Anzahl wird im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; mitgezählt. Da dieses Register mit dem ASCII Code von &#039;0&#039; initialisiert wurde, und dieser ASCII Code bei jedem Schleifendurchlauf um 1 erhöht wird, können wir das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; direkt zur Ausgabe des Zeichens für die Hunderterstelle durch die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; benutzen. Völlig analog funktioniert auch die Ausgabe der Zehnerstelle.&lt;br /&gt;
&lt;br /&gt;
===Unterdrückung von führenden Nullen===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung: Diese Routine ist fehlerhaft&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt jede Zahl im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; immer mit 3 Stellen aus. Führende Nullen werden nicht unterdrückt. Möchte man dies ändern, so ist das ganz leicht möglich: Vor Ausgabe der Hunderterstelle bzw. Zehnerstelle muss lediglich überprüft werden, ob die Entsprechende Ausgabe eine &#039;0&#039; wäre. Ist sie das, so wird die Ausgabe übersprungen. Lediglich in der Einerstelle wird jede Ziffer wie errechnet ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ...&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben, wenn&lt;br /&gt;
                                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_2a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_2a:&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&lt;br /&gt;
           ...&lt;br /&gt;
                              ; die Zehnerstelle ausgeben, wenn&lt;br /&gt;
                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_4a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_4a:&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Verfahren, die einzelnen Stellen durch Subtraktion zu bestimmen, ist bei kleinen Zahlen eine durchaus gängige Alternative. Vor allem dann, wenn keine hardwaremäßige Unterstützung für Multiplikation und Division zur Verfügung steht. Ansonsten könnte man die die einzelnen Ziffern auch durch Division bestimmen. Das Prinzip ist folgendes (beispielhaft an der Zahl 52783 gezeigt)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   52783 / 10          -&amp;gt; 5278&lt;br /&gt;
   52783 - 5278 * 10   -&amp;gt;          3&lt;br /&gt;
&lt;br /&gt;
   5278 / 10           -&amp;gt; 527&lt;br /&gt;
   5278 - 527 * 10     -&amp;gt;          8&lt;br /&gt;
&lt;br /&gt;
   527 / 10            -&amp;gt; 52&lt;br /&gt;
   527 - 52 * 10       -&amp;gt;          7&lt;br /&gt;
&lt;br /&gt;
   52 / 10             -&amp;gt; 5&lt;br /&gt;
   52 - 5 * 10         -&amp;gt;          2&lt;br /&gt;
&lt;br /&gt;
   5 / 10              -&amp;gt; 0&lt;br /&gt;
   5 - 0 * 10          -&amp;gt;          5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Prinzip ist also die Restbildung bei einer fortgesetzten Division durch 10, wobei die einzelnen Ziffern in umgekehrter Reihenfolge ihrer Wertigkeit entstehen. Dadurch hat man aber ein Problem: Damit die Zeichen in der richtigen Reihenfolge ausgegeben werden können, muß man sie meistens zwischenspeichern um sie in der richtigen Reihenfole ausgeben zu können. Wird die Zahl in einem Feld von immer gleicher Größe ausgegeben, dann kann man auch die Zahl von rechts nach links ausgeben (bei einem LCD ist das möglich).&lt;br /&gt;
&lt;br /&gt;
===Hexadezimal ausgeben===&lt;br /&gt;
&lt;br /&gt;
Zu guter letzt hier noch eine Funktion, die eine Zahl aus dem Register &#039;&#039;&#039;temp1&#039;&#039;&#039; in hexadezimaler Form ausgibt. Die Funktion weist keine Besonderheiten auf und sollte unmittelbar verständlich sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen hexadezimal ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number_hex:&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           rcall lcd_number_hex_digit&lt;br /&gt;
           swap  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_number_hex_digit:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
           andi  temp1, $0F&lt;br /&gt;
           cpi   temp1, 10&lt;br /&gt;
           brlt  lcd_number_hex_digit_1&lt;br /&gt;
           subi  temp1, -( &#039;A&#039; - &#039;9&#039; - 1 ) ; es wird subi mit negativer Konstante verwendet, weil es kein addi gibt&lt;br /&gt;
lcd_number_hex_digit_1:&lt;br /&gt;
           subi  temp1, -&#039;0&#039;               ; ditto&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           &lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Eine 16-Bit Zahl aus einem Registerpärchen ausgeben===&lt;br /&gt;
&lt;br /&gt;
Um eine 16 Bit Zahl auszugeben wird wieder das bewährte Schema benutzt die einzelnen Stellen durch Subtraktion abzuzählen. Da es sich hierbei allerdings um eine 16 Bit Zahl handelt, müssen die Subtraktionen als 16-Bit Arithmetik ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 16 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp2 (low Byte) / temp3 (high Byte)&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number16:&lt;br /&gt;
           push  temp1&lt;br /&gt;
           push  temp2&lt;br /&gt;
           push  temp3&lt;br /&gt;
&lt;br /&gt;
; die Zehntausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number0:&lt;br /&gt;
           subi  temp2, low(10000)&lt;br /&gt;
           sbci  temp3, high(10000)&lt;br /&gt;
           brcs  lcd_number1&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number0&lt;br /&gt;
&lt;br /&gt;
; .. und ausgeben&lt;br /&gt;
lcd_number1:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-10000)&lt;br /&gt;
           sbci  temp3, high(-10000)&lt;br /&gt;
&lt;br /&gt;
; die Tausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number2:&lt;br /&gt;
           subi  temp2, low(1000)&lt;br /&gt;
           sbci  temp3, high(1000)&lt;br /&gt;
           brcs  lcd_number3&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number2&lt;br /&gt;
&lt;br /&gt;
; ... und ausgeben&lt;br /&gt;
lcd_number3:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-1000)&lt;br /&gt;
           sbci  temp3, high(-1000)&lt;br /&gt;
&lt;br /&gt;
; Als nächtes kommt die Hunderterstelle drann&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number4:&lt;br /&gt;
           subi  temp2, low(100)&lt;br /&gt;
           sbci  temp3, high(100)&lt;br /&gt;
           brcs  lcd_number5&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number4&lt;br /&gt;
&lt;br /&gt;
; und ausgeben&lt;br /&gt;
lcd_number5:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100&lt;br /&gt;
&lt;br /&gt;
; bleiben noch die Zehner&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number6:&lt;br /&gt;
           subi  temp2, 10&lt;br /&gt;
           brcs  lcd_number7&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number6&lt;br /&gt;
&lt;br /&gt;
; ausgeben ...&lt;br /&gt;
lcd_number7:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10&lt;br /&gt;
&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
           add   temp1, temp2&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
; fertig. Stack wieder aufräumen&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           pop   temp3&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Der überarbeitete, komplette Code==&lt;br /&gt;
&lt;br /&gt;
Hier also die komplett überarbeitete Version der LCD Funktionen.&lt;br /&gt;
&lt;br /&gt;
Die für die Benutzung relevanten Funktionen&lt;br /&gt;
* &#039;&#039;&#039;lcd_init&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_clear&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_home&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_data&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_flash_string&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number_hex&#039;&#039;&#039;&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; übergeben, wobei &#039;&#039;&#039;temp1&#039;&#039;&#039; vom Usercode definiert werden muss.&lt;br /&gt;
&lt;br /&gt;
[[Media:lcd-routines.asm|Download lcd-routines.asm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Stack|&lt;br /&gt;
zurücklink=AVR-Tutorial: Stack|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Interrupts|&lt;br /&gt;
vorlink=AVR-Tutorial: Interrupts}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister SREG) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register temp1 übergeben, wobei temp1 vom Usercode definiert werden muss. &lt;br /&gt;
&lt;br /&gt;
der progger hat vergessen das Z reg in den sub&#039;s lcd_flash_string ...&lt;br /&gt;
zu sichern, aber danke für den Einstig&lt;br /&gt;
&lt;br /&gt;
Lars&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27994</id>
		<title>AVR-Tutorial: LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27994"/>
		<updated>2008-05-16T03:01:04Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Initialisierung des Displays */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kaum ein elektronisches Gerät kommt heutzutage noch ohne ein LCD daher. Ist doch auch praktisch, Informationen im Klartext anzeigen zu können, ohne irgendwelche LEDs blinken zu lassen. Kein Wunder also, dass die häufigste Frage in Mikrocontroller-Foren ist: &amp;quot;Wie kann ich ein LCD anschließen?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-Bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-Bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-Bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-Bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-Bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-Bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4 Bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert. Ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitet hat. Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt, da wir wissen, welche Anschlüsse das LCD benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Pinnummer&amp;lt;BR&amp;gt;LCD || Bezeichnung || Anschluss&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |1 || Vss || GND&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |2 || Vcc || 5V&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |3 || Vee || GND oder [[Potentiometer | Poti]] &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |4 || RS || PD4 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |5 || RW || GND &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |6 || E || PD5 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |7 || DB0 || nicht angeschlossen &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |8 || DB1 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |9 || DB2 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |10 || DB3 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |11 || DB4 || PD0 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |12 || DB5 || PD1 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |13 || DB6 || PD2 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |14 || DB7 || PD3 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |15 || A || Vorsicht! Meistens nicht direkt an +5V anschließbar,&amp;lt;BR&amp;gt;nur über einen Vorwiderstand (z.B. 33&amp;amp;Omega;)!&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |16 || K || GND&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Ok alles ist verbunden. Wenn man jetzt den Strom einschaltet, sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display?&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung des LCDs im 4-Bit-Modus ==&lt;br /&gt;
&lt;br /&gt;
Um ein Byte zu übertragen, muss man es erstmal in die beiden Nibbles zerlegen, die getrennt übertragen werden. Da das obere Nibble (Bit 4 - Bit 7) als erstes übertragen wird, die 4 Datenleitungen jedoch an die vier unteren Bits des Port D angeschlossen sind, muss man die beiden Nibbles des zu übertragenden Bytes erstmal vertauschen. Der AVR kennt dazu praktischerweise einen eigenen Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           swap r16               ; vertauscht die beiden Nibbles von r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus 0b00100101 wird so z.B. 0b01010010. &lt;br /&gt;
&lt;br /&gt;
Jetzt sind die Bits für die erste Phase der Übertragung an der richtigen Stelle. Trotzdem wollen wir das Ergebnis nicht einfach so mit &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; an den Port geben. Um die Hälfte des Bytes, die jetzt nicht an die Datenleitungen des LCDs gegeben wird auf null zu setzen, verwendet man folgenden Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           andi r16, 0b00001111   ; Nur die vier unteren (mit 1 markierten)&lt;br /&gt;
                                  ; Bits werden übernommen, alle anderen werden null&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also: Das obere Nibble wird erst mit dem unteren vertauscht, damit es unten ist. Dann wird das obere (das wir jetzt noch nicht brauchen) auf null gesetzt. &lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem LCD noch mitteilen, ob wir Daten oder Befehle senden wollen. Das machen wir, indem wir das Bit, an dem RS angeschlossen ist (PD4), auf 0 (Befehl senden) oder auf 1 (Daten senden) setzen. Um ein Bit in einem normalen Register zu setzen, gibt es den Befehl sbr (Set Bit in Register). Dieser Befehl unterscheidet sich jedoch von sbi (das nur für IO-Register gilt) dadurch, dass man nicht die Nummer des zu setzenden Bits angibt, sondern eine Bitmaske. Das geht so: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbr r16, 0b00010000     ; Bit 4 setzen, alle anderen Bits bleiben gleich&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RS ist an PD4 angeschlossen. Wenn wir r16 an den Port D ausgeben, ist RS jetzt also high und das LCD erwartet Daten anstatt von Befehlen. &lt;br /&gt;
&lt;br /&gt;
Das Ergebnis können wir jetzt endlich direkt an den Port D übergeben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           out PORTD, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss vorher der Port D auf Ausgang geschalten werden, indem man 0xFF ins Datenrichtungsregister DDRD schreibt. &lt;br /&gt;
&lt;br /&gt;
Um dem LCD zu signalisieren, dass es das an den Datenleitungen anliegende Nibble übernehmen kann, wird die E-Leitung (Enable, an PD5 angeschlossen) auf high und kurz darauf wieder auf low gesetzt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbi PORTD, 5              ; Enable high&lt;br /&gt;
           nop                       ; 3 Taktzyklen warten (&amp;quot;nop&amp;quot; = nichts tun)&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5              ; Enable wieder low&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die eine Hälfte des Bytes wäre damit geschafft! Die andere Hälfte kommt direkt hinterher: Alles, was an der obenstehenden Vorgehensweise geändert werden muss, ist, das &amp;quot;swap&amp;quot; (Vertauschen der beiden Nibbles) wegzulassen.&lt;br /&gt;
&lt;br /&gt;
== Initialisierung des Displays ==&lt;br /&gt;
&lt;br /&gt;
Allerdings gibt es noch ein Problem. Wenn ein LCD eingeschaltet wird, dann läuft es zunächst im 8 Bit Modus. Irgendwie muss das Display initialisiert und auf den 4 Bit Modus umgeschaltet werden, und zwar nur mit den 4 zur Verfügung stehenden Datenleitungen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn es Probleme gibt, dann meistens an diesem Punkt. Die &amp;quot;kompatiblen&amp;quot; Kontroller sind gelegentlich doch nicht 100% identisch. Es lohnt sich, das Datenblatt (siehe Weblinks im Artikel [[LCD]]) genau zu lesen, in welcher Reihenfolge und mit welchen Abständen (Delays) die Initialisierungbefehle gesendet werden. Eine weitere Hilfe können Ansteuerungsbeispiele in Forenbeiträgen geben z.B.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79609#664268 (A) KS0066U oder Ähnliche --- LCD Treiber]&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 4 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Achtung: Im Folgenden sind alle Bytes aus Sicht des LCD-Kontrollers angegeben! Da LCD-seitig nur die Leitungen DB4 - DB7 verwendet werden, ist daher immer nur das höherwertige Nibble gültig. Durch die Art der Verschaltung (DB4 - DB7 wurde auf dem PORT an PD0 bis PD3 angeschlossen) ergibt sich eine Verschiebung, so dass das am Kontroller auszugebende Byte nibblemässig vertauscht ist!&lt;br /&gt;
&lt;br /&gt;
Die Sequenz, aus Sicht des Kontrollers, sieht so aus:&lt;br /&gt;
&lt;br /&gt;
* nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 4.1ms warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 100µs warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* $2 ins Steuerregister schreiben (RS = 0), dadurch wird auf 4 Bit Daten umstellt&lt;br /&gt;
* ab jetzt muss für die Übertragung eines Bytes jeweils zuerst das höherwertige Nibble und dann das niederwertige Nibble übertragen werden, wie oben beschrieben&lt;br /&gt;
* Mit dem Konfigurier-Befehl $20 das Display konfigurieren (4-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 8 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Der Vollständigkeit halber hier noch die notwendige Initialiserungssequenz für den 8 Bit Modus. Da hier die Daten komplett als 1 Byte übertragen werden können, sind einige Klimmzüge wie im 4 Bit Modus nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
* nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 4.1ms warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 100µs warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mit dem Konfigurier-Befehl 0x30 das Display konfigurieren (8-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
== Routinen zur LCD-Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
Die Routinen zur Kommunikation mit dem LCD sehen also so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000 (Anm.1)&lt;br /&gt;
           out PORTD, temp1             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000&lt;br /&gt;
           out PORTD, temp2             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out PORTD, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
 ;&lt;br /&gt;
 ; Bei höherem Takt (&amp;gt;= 8 MHz) kann es notwendig sein, &lt;br /&gt;
 ; vor dem Enable High 1-2 Wartetakte (nop) einzufügen. &lt;br /&gt;
 ; Siehe dazu http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi PORTD, 5                 ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5                 ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
&lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi  temp3,50&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall  delay5ms&lt;br /&gt;
           dec  temp3&lt;br /&gt;
           brne powerupwait&lt;br /&gt;
           ldi temp1, 0b00000011        ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out PORTD, temp1             ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00000010        ; 4bit-Modus einstellen&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00101000        ; 4Bit / 2 Zeilen / 5x8&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00001100        ; Display ein / Cursor aus / kein Blinken&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00000100        ; inkrement / kein Scrollen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi temp1, 0b00000001   ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi temp1, 0b00000010   ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anm.1: Siehe [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
Weitere Funktionen (wie z.B. Cursorposition verändern) sollten mit Hilfe der [[AVR-Tutorial:_LCD#Welche_Befehle_versteht_das_LCD.3F|Befehlscodeliste]] nicht schwer zu realisieren sein. Einfach den Code in temp laden, lcd_command aufrufen und ggf. eine Pause einfügen. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man die LCD-Ansteuerung auch an einen anderen Port des Mikrocontrollers &amp;quot;verschieben&amp;quot;: Wenn das LCD z.B. an Port B angeschlossen ist, dann reicht es im Programm alle &amp;quot;PORTD&amp;quot; durch &amp;quot;PORTB&amp;quot; und &amp;quot;DDRD&amp;quot; durch &amp;quot;DDRB&amp;quot; zu ersetzen. &lt;br /&gt;
&lt;br /&gt;
Wer eine höhere Taktfrequenz als 4 MHz verwendet, der sollte daran denken die Dauer der Verzögerungsschleifen anzupassen.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das diese Routinen zur Anzeige von Text verwendet, kann z.B. so aussehen (die Datei lcd-routines.asm muss sich im gleichen Verzeichnis befinden). Nach der Initialisierung wird zuerst der Displayinhalt gelöscht. Um dem LCD ein Zeichen zu schicken, lädt man es in temp1 und ruft die Routine &amp;quot;lcd_data&amp;quot; auf. Das folgende Beispiel zeigt das Wort &amp;quot;Test&amp;quot; auf dem LCD an. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/lcd-test.asm Download lcd-test.asm] &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;
&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, 0xFF    ; Port D = Ausgang&lt;br /&gt;
           out DDRD, temp1&lt;br /&gt;
&lt;br /&gt;
           rcall lcd_init     ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear    ; Display löschen&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;T&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;e&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           &lt;br /&gt;
           ldi temp1, &#039;s&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;t&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD-Routinen werden hier eingefügt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für längere Texte ist die Methode, jedes Zeichen einzeln in das Register zu laden und &amp;quot;lcd_data&amp;quot; aufzurufen natürlich nicht sehr praktisch. Dazu später aber mehr.&lt;br /&gt;
&lt;br /&gt;
Bisher wurden in Register immer irgendwelche Zahlenwerte geladen, aber in diesem Programm kommt plötzlich die Anweisung&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
vor. Wie ist diese zu verstehen? Passiert hier etwas grundlegend anderes als beim Laden einer Zahl in ein Register?&lt;br /&gt;
&lt;br /&gt;
Die Antwort darauf lautet: Nein. Auch hier wird letztendlich nur eine Zahl in ein Register geladen. Der Schlüssel zum Verständnis beruht darauf, dass zum LCD, so wie zu allen Ausgabegeräten, für die Ausgabe von Texten immer nur Zahlen übertragen werden, sog. Codes. Zum Beispiel könnte man vereinbaren, dass ein LCD, wenn es den Ausgabecode 65 erhält ein &#039;A&#039; anzeigt, bei einem Ausgabecode von 66 ein &#039;B&#039; usw. Naturgemäß gibt es daher viele verschiedene Code-Buchstaben Zuordnungen. Damit hier etwas Ordnung in das potentielle Chaos kommt, hat man sich bereits in der Steinzeit der Programmierung auf bestimmte Codetabellen geeinigt, von denen die verbreitetste sicherlich die ASCII-Zuordnung ist.&lt;br /&gt;
&lt;br /&gt;
==ASCII==&lt;br /&gt;
&lt;br /&gt;
ASCII steht für &#039;&#039;American Standard Code for Information Interchange&#039;&#039; und ist ein standardisierter Code zu Zeichenumsetzung. Die Codetabelle sieht hexadezimal dabei wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;.0&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x1&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x2&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x3&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x4&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x6&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x7&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x8&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x9&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xA&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xB&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xC&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xD&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xE&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xF&amp;lt;/th&amp;gt;&amp;lt;br&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;&#039;&#039;&#039;0x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NUL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SOH&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;STX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EOT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ENQ&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ACK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BEL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;HT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;VT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CR&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SO&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SI&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;&#039;&#039;&#039;1x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DLE&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NAK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SYN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CAN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EM&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SUB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ESC&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;US&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;&#039;&#039;&#039;2x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SP&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;!&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;#&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;%&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;amp;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;(&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;)&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;*&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;+&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;,&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;.&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;3x&#039;&#039;&#039;&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;&amp;lt;td&amp;gt;3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;9&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;=&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;gt;&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;4x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;B&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;C&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;D&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;F&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;G&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;H&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;I&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;J&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;L&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;M&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;N&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;O&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;&#039;&#039;&#039;5x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;P&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;R&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;S&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;T&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;U&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;V&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;W&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;X&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;[&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;\&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;]&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;^&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;6x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;`&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;a&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;b&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;c&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;d&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;e&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;f&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;g&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;h&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;i&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;j&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;k&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;l&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;m&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;n&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;o&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;&#039;&#039;&#039;7x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;p&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;r&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;s&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;t&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;u&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;v&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;w&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;{&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;|&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;~&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DEL&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 ersten beiden Zeilen enthalten die Codes für einige Steuerzeichen, ihre vollständige Beschreibung würde hier zu weit führen. Das Zeichen &#039;&#039;&#039;SP&#039;&#039;&#039; steht für ein &#039;&#039;Space&#039;&#039;, also ein Leerzeichen. &#039;&#039;&#039;BS&#039;&#039;&#039; steht für &#039;&#039;Backspace&#039;&#039;, also ein Zeichen zurück. &#039;&#039;&#039;DEL&#039;&#039;&#039; steht für &#039;&#039;Delete&#039;&#039;, also das Löschen eines Zeichens. &#039;&#039;&#039;CR&#039;&#039;&#039; steht für &#039;&#039;Carriage Return&#039;&#039;, also wörtlich: der Wagenrücklauf (einer Schreibmaschine), während &#039;&#039;&#039;LF&#039;&#039;&#039; für &#039;&#039;Line feed&#039;&#039;, also einen Zeilenvorschub steht.&lt;br /&gt;
&lt;br /&gt;
Der Assembler kennt diese Codetabelle und ersetzt die Zeile&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, $54&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
was letztendlich auch der Lesbarkeit des Programmes zugute kommt. Funktional besteht kein Unterschied zwischen den beiden Anweisungen. Beide bewirken, dass das Register temp1 mit dem Bitmuster 01010100 ( = hexadezimal 54, = dezimal 84 oder eben der ASCII Code für &#039;&#039;&#039;T&#039;&#039;&#039;) geladen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das LCD wiederrum kennt diese Code-Tabelle ebenfalls und wenn es über den Datenbus die Codezahl $54 zur Anzeige empfängt, dann schreibt es ein &#039;&#039;&#039;T&#039;&#039;&#039; an die aktuelle Cursorposition. Genauer gesagt, weiss das LCD nichts von einem &#039;&#039;&#039;T&#039;&#039;&#039;. Es sieht einfach in seinen internen Tabellen nach, welche Pixel beim Empfang der Codezahl $54 auf schwarz zu setzen sind. &#039;Zufällig&#039; sind das genau jene Pixel, die für uns Menschen ein &#039;&#039;&#039;T&#039;&#039;&#039; ergeben.&lt;br /&gt;
&lt;br /&gt;
==Welche Befehle versteht das LCD?==&lt;br /&gt;
&lt;br /&gt;
Auf dem LCD arbeitet ein Kontroller vom Typ HD44780. Diesen Kontroller versteht eine Reihe von Befehlen, die allesamt mittels lcd_command gesendet werden können. Ein Kommando ist dabei nichts anderes als ein Befehlsbyte, indem die verschiedenen Bits verschiedene Bedeutung besitzen:&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 0 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 1 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;der Zustand dieses Bits ist egal&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;sonstige Buchstaben&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;das Bit muss je nach gewünschter Funktionalität gesetzt werden. Die mögliche Funktionalität des jeweiligen Bits geht aus der Befehlsbeschreibung hervor&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Kommando &#039;ON/OFF Control&#039; soll benutzt werden um das Display einzuschalten, der Cursor soll eingeschaltet werden und der Cursor soll blinken.&lt;br /&gt;
Das Befehlsbyte ist so aufgebaut:&lt;br /&gt;
   0b00001dcb&lt;br /&gt;
Aus der Befehlsbeschreibung entnimmt man:&lt;br /&gt;
* Display ein bedeutet, dass an der Bitposition d eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor ein bedeutet, dass an der Bitposition c ein 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor blinken bedeutet, dass an der Bitposition b eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
Das dafür zu übertragende Befehlsbyte hat also die Gestalt 0b00001111 oder in hexadezimaler Schreibweise $0F&lt;br /&gt;
&lt;br /&gt;
===Clear display: 0b00000001===&lt;br /&gt;
&lt;br /&gt;
Die Anzeige wird gelöscht und der Ausgabecursor kehrt an die Home Position (links, erste Zeile) zurück&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Cursor home: 0b0000001x===&lt;br /&gt;
&lt;br /&gt;
Der Cursor kehrt an die Home Position (links, erste Zeile) zurück. Ein verschobenes Display wird auf die Grundeinstellung zurückgesetzt.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs bis 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Entry mode: 0b000001is===&lt;br /&gt;
&lt;br /&gt;
Legt die Cursor Richtung fest sowie eine mögliche Verschiebung des Displays fest&lt;br /&gt;
* i = 1, Cursorposition bei Ausgabe eines Zeichens erhöhen&lt;br /&gt;
* i = 0, Cursorposition bei Ausgabe eines Zeichens vermindern&lt;br /&gt;
* s = 1, Display wird gescrollt, wenn der Cursor das Ende/Anfang, je nach Einstellung von i, erreicht hat.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===On/off control: 0b00001dcb===&lt;br /&gt;
&lt;br /&gt;
Display insgesamt ein/ausschalten; den Cursor ein/ausschalten; den Cursor auf blinken schalten/blinken aus. Wenn das Display ausgeschaltet wird, geht der Inhalt des Displays nicht verloren. Der vorher angezeigte Text wird nach wiedereinschalten erneut angezeigt.&lt;br /&gt;
Ist der Cursor eingeschaltet, aber Blinken ausgeschaltet, so wird der Cursor als Cursorzeile in Pixelzeile 8 dargestellt. Ist Blinken eingeschaltet, wird der Cursor als blinkendes ausgefülltes Rechteck dargestellt, welches abwechselnd mit dem Buchstaben an dieser Stelle angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
* d = 0, Display aus&lt;br /&gt;
* d = 1, Display ein&lt;br /&gt;
* c = 0, Cursor aus&lt;br /&gt;
* c = 1, Cursor ein&lt;br /&gt;
* b = 0, Cursor blinken aus&lt;br /&gt;
* b = 1, Cursor blinken ein&lt;br /&gt;
 &lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Cursor/Scrollen: 0b0001srxx===&lt;br /&gt;
&lt;br /&gt;
Bewegt den Cursor oder scrollt das Display um eine Position entweder nach rechts oder nach links.&lt;br /&gt;
&lt;br /&gt;
* s = 1, Display scrollen&lt;br /&gt;
* s = 0, Cursor bewegen&lt;br /&gt;
* r = 1, nach rechts&lt;br /&gt;
* r = 0, nach links &lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Konfiguration: 0b001dnfxx===&lt;br /&gt;
&lt;br /&gt;
Einstellen der Interface Art, Modus, Font&lt;br /&gt;
* d = 0, 4-Bit Interface&lt;br /&gt;
* d = 1, 8-Bit Interface&lt;br /&gt;
* n = 0, 1 zeilig&lt;br /&gt;
* n = 1, 2 zeilig&lt;br /&gt;
* f = 0, 5x7 Pixel&lt;br /&gt;
* f = 1, 5x11 Pixel&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Character RAM Address Set: 0b01aaaaaa===&lt;br /&gt;
&lt;br /&gt;
Mit diesem Kommando werden maximal 8 selbst definierte Zeichen definiert. Dazu wird der Character RAM Zeiger auf den Anfang des Character Generator (CG) RAM gesetzt und das Zeichen durch die Ausgabe von 8 Byte definiert. Der Adress Zeiger wird nach Ausgabe jeder Pixelzeile (8Bit) vom LCD selbst erhöht. Nach Beendigung der Zeichendefinition muss die Schreibposition explizit mit dem Kommando &amp;quot;Display RAM Address Set&amp;quot; wieder in den DD-RAM Bereich gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
aaaaaa 6-bit CG RAM Adresse&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Display RAM Address Set: 0b1aaaaaaa===&lt;br /&gt;
&lt;br /&gt;
Den Cursor neu positionieren. Display Data (DD) Ram ist vom Character Generator (CG) Ram unabhängig. Der Adresszeiger wird bei Ausgabe eines Zeichens ins DD Ram automatisch erhöht. Das Display verhält sich so, als ob eine Zeile immer aus 32 logischen Zeichen besteht, von der, je nach konkretem Displaytyp (16 Zeichen, 20 Zeichen) immer nur ein Teil sichtbar ist.&lt;br /&gt;
&lt;br /&gt;
aaaaaaa 7-bit DD RAM Adresse. Auf 2-zeiligen Displays (und den meisten 16x1 Displays), kann die Adressangabe wie folgt interpretiert werden&lt;br /&gt;
&lt;br /&gt;
1laaaaaa&lt;br /&gt;
* l = Zeilennummer (0 oder 1)&lt;br /&gt;
* a = 6-Bit Spaltennummer&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
==Einschub: Code aufräumen==&lt;br /&gt;
&lt;br /&gt;
Es wird Zeit sich einmal etwas kritisch mit den bisher geschriebenen Funktionen auseinander zu setzen.&lt;br /&gt;
&lt;br /&gt;
===Portnamen aus dem Code herausziehen===&lt;br /&gt;
&lt;br /&gt;
Wenn wir die LCD-Funktionen einmal genauer betrachten, dann fällt sofort auf, daß über die Funktionen verstreut immer wieder das &#039;&#039;&#039;PORTD&#039;&#039;&#039; sowie einzelne Zahlen für die Pins an diesem Port auftauchen. Wenn das LCD an einem anderen Port betrieben werden soll, oder sich die Pin-Belegung ändert, dann muß an all diesen Stellen eine Anpassung vorgenommen werden. Dabei darf keine einzige Stelle übersehen werden, ansonsten würden die LCD-Funktionen nicht oder nicht vollständig funktionieren.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit dem vorzubeugen ist es, diese immer gleichbleibenden Dinge an den Anfang der LCD-Funktionen vorzuziehen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
.equ PIN_E    = 5&lt;br /&gt;
.equ PIN_RS   = 4&lt;br /&gt;
&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp1          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp2          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi LCD_PORT, PIN_E          ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi LCD_PORT, PIN_E          ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
 &lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf Ausgang&lt;br /&gt;
           out   LCD_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi   temp3,6&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           dec   temp3&lt;br /&gt;
           brne  powerupwait&lt;br /&gt;
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out   LCD_PORT, temp1        ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00101000      ; 4 Bot, 2 Zeilen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00001100      ; Display on, Cursor off&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00000100      ; endlich fertig&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi   temp1, 0b00000010      ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels &#039;&#039;&#039;.equ&#039;&#039;&#039; werden mit dem Assembler Textersetzungen vereinbart. Der Assembler ersetzt alle Vorkomnisse des Quelltextes durch den zu ersetzenden Text. Dadurch ist es zb. möglich alle Vorkommnisse von &#039;&#039;&#039;PORTD&#039;&#039;&#039; durch &#039;&#039;&#039;LCD_PORT&#039;&#039;&#039; auszutauschen. Wird das LCD an einen anderen Port, zb. &#039;&#039;&#039;PORTB&#039;&#039;&#039; gelegt, dann genügt es, einzig und alleine die Zeilen&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTB&lt;br /&gt;
.equ LCD_DDR  = DDRB&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu ersetzen. Der Assembler sorgt dann dafür, dass diese Portänderung an den relevanten Stellen im Code über die Textersetzungen einfliesst. Selbiges natürlich mit der Pin-Zuordnung.&lt;br /&gt;
&lt;br /&gt;
===Registerbenutzung===&lt;br /&gt;
&lt;br /&gt;
Bei diesen Funktionen mussten einige Register des Prozessors benutzt werden um darin Zwischenergebnisse zu speichern bzw. zu bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss dabei natürlich, dass es zu keinen Überschneidungen kommt. Solange nur jede Funktion jeweils für sich betrachtet wird, ist das kein Problem. In 20 oder 30 Code-Zeilen kann man gut verfolgen, welches Register wofür benutzt wird. Schwieriger wird es, wenn Funktionen wiederrum andere Funktionen aufrufen, die ihrerseits wieder Funktionen aufrufen usw. Jede dieser Funktionen benutzt einige Register und mit zunehmender Programmgröße wird es immer schwieriger zu verfolgen, welches Register zu welchem Zeitpunkt wofür benutzt wird.&lt;br /&gt;
&lt;br /&gt;
Speziell bei Basisfunktionen, wie diesen LCD-Funktionen, ist es daher oft ratsam, dafür zu sorgen, daß jede Funktion die Register wieder in dem Zustand hinterlässt, indem sie sie auch vorgefunden hat. Wir benötigen dazu wieder den Stack, auf dem die Registerinhalte bei Betreten einer Funktion zwischengespeichert werden und von dem die Register bei Verlassen einer Funktion wiederhergestellt werden.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir die Funktion&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion verändert das Register temp1. Um das Register abzusichern schreiben wir die Funktion um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           push  temp1                  ; temp1 auf dem Stack sichern&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           pop   temp1                  ; temp1 vom Stack wiederherstellen&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am besten hält man sich an die Regel: Jede Funktion ist dafür zuständig die Register zu sichern und wiederherzustellen, die sie auch selbst verändert. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; ruft die Funktionen &#039;&#039;&#039;lcd_command&#039;&#039;&#039; und &#039;&#039;&#039;delay5ms&#039;&#039;&#039; auf. Wenn diese Funktionen selbst wieder Register verändern (und das tun sie), so ist es die Aufgabe dieser Funktionen, sich um die Sicherung und das Wiederherstellen der entsprechenden Register zu kümmern. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; sollte sich nicht darum kümmern müssen. Auf diese Weise ist das Schlimmste, das einem passieren kann, das ein paar Register unnütz gesichert und wiederhergestellt werden. Das kostet zwar etwas Rechenzeit und etwas Speicherplatz auf dem Stack, ist aber immer noch besser als das andere Extrem: Nach einem Funktionsaufruf haben einige Register nicht mehr den Wert den sie haben sollten und das Programm rechnet mit falschen Zahlen weiter.&lt;br /&gt;
&lt;br /&gt;
===Lass den Assembler rechnen===&lt;br /&gt;
Betrachtet man den Code genauer, so fallen einige konstante Zahlenwerte auf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Das vorangestellte $ kennzeichnet die Zahl als Hexadezimalzahl) Der Code benötigt eine Warteschleife die mindestens 50µs dauert. Die beiden Befehle innerhalb der Schleife benötigen 3 Takte: 1 Takt für den &#039;&#039;&#039;dec&#039;&#039;&#039; und der &#039;&#039;&#039;brne&#039;&#039;&#039; benötigt 2 Takte wenn die Bedingung zutrifft, der Branch also genommen wird. Bei 4 Mhz werden also 4000000 / 3 * 50 / 1000000 = 66.6 Durchläufe durch die Schleife benötigt um eine Verzögerungszeit von 50µs (0.000050 Sekunden) zu erreichen, hexadezimal ausgedrückt: $42&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist: Bei anderen Taktfrequenzen müsste man nun jedesmal diese Berechnung machen und den entsprechenden Zahlenwert einsetzen. Das kann aber der Assembler genausogut erledigen. Am Anfang des Codes wird ein Eintrag definiert, der die Taktfrequenz festlegt. Traditionell heist dieser Eintrag &amp;lt;i&amp;gt;XTAL&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ XTAL  = 4000000&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 50 / 3 ) / 1000000&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An einer anderen Codestelle gibt es weitere derartige magische Zahlen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was geht hier vor?&lt;br /&gt;
Die innere Schleife benötigt wieder 3 Takte pro Durchlauf. Bei $C9 = 201 Durchläufen werden also 201 * 3 = 603 Takte verbraucht. In der äußeren Schleife kommen pro Durchlauf alo 1 + 603 + 1 + 2 = 607 Takte verbraucht. Da die äußere Schleife $21 = 33 mal wiederholt wird, werden 20031 Takte verbraucht. Bei 4Mhz benötigt der Prozessor 20031 / 4000000 = 0.005007 Sekunden, also 5 ms.&lt;br /&gt;
Wird der Wiederholwert für die innere Schleife bei $C9 belassen, so werden 4000000 / 607 * 5 / 1000 Wiederholungen der äusseren Schleife benötigt. Auch diese Berechnung kann wieder der Assembler übernehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 5 / 607 ) / 1000&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein kleines Problem kann bei der Verwendung dieses Verfahrens entstehen: Bei hohen Taktfrequenzen und großen Wartezeiten kann der berechnete Wert größer als 255 werden und man bekommt die Fehlermeldung &amp;quot;Operand(s) out of range&amp;quot; beim Assemblieren. Dieser Fall tritt zum Beispiel für obige Konstruktion bei einer Taktfrequenz von 16 MHz ein (genauer gesagt ab 15,3 MHz), während darunter XTAL beliebig geändert werden kann. Als einfachste Lösung bietet es sich an, die Zahl der Takte pro Schleifendurchlauf durch das Einfügen von &#039;&#039;&#039;nop&#039;&#039;&#039; zu erhöhen und die Berechnungsvorschrift anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Ausgabe eines konstanten Textes ==&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde schon einmal ein Text ausgegeben. Dies geschah durch Ausgabe von einzelnen Zeichen. Das können wir auch anders machen. Wir können den Text im Speicher ablegen und eine Funktion schreiben, die die einzelnen Zeichen aus dem Speicher holt und ausgibt. Dabei erhebt sich aber eine Fragestellung: Woher weiß die Funktion eigentlich, wie lange der Text ist? Die Antwort darauf lautet: Sie kann es nicht wissen. Wir müssen irgendwelche Vereinbarungen treffen, woran die Funktion erkennen kann, dass der Text zu Ende ist. Im Wesentlichen werden dazu 2 Methoden benutzt:&lt;br /&gt;
* Der Text enthält ein spezielles Zeichen, welches das Ende des Textes markiert&lt;br /&gt;
* Wir speichern nicht nur den Text selbst, sondern auch die Länge des Textes&lt;br /&gt;
Mit einer der beiden Methoden ist es der Textausgabefunktion dann ein Leichtes, den Text vollständig auszugeben.&lt;br /&gt;
&lt;br /&gt;
Wir werden uns im Weiteren dafür entscheiden, ein spezielles Zeichen, eine 0, dafür zu benutzen. Die Ausgabefunktionen werden dann etwas einfacher, als wenn bei der Ausgabe die Anzahl der bereits ausgegebenen Zeichen mitgezählt werden muss.&lt;br /&gt;
&lt;br /&gt;
Den Text selbst speichern wir im Flash-Speicher, also dort wo auch das Programm gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Einen konstanten Text aus dem Flash Speicher&lt;br /&gt;
 ; ausgeben. Der Text wird mit einer 0 beendet&lt;br /&gt;
lcd_flash_string:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_1:&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           rjmp  lcd_flash_string_1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_2:&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion benutzt den Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039; um das jeweils nächste Zeichen aus dem Flash Speicher in ein Register zur Weiterverarbeitung zu laden. Dazu wird der sog. &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; benutzt. So nennt man das Registerpaar &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;. Nach jedem Ladevorgang wird dabei durch den Befehl&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
dieser Z-Pointer um 1 erhöht. Mittels &#039;&#039;&#039;cpi&#039;&#039;&#039; wird das in das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; geladene Zeichen mit 0 verglichen. &#039;&#039;&#039;cpi&#039;&#039;&#039; vergleicht die beiden Zahlen und merkt sich das Ergebnis in einem speziellen Register in Form von Status Bits. &#039;&#039;&#039;cpi&#039;&#039;&#039; zieht dabei ganz einfach die beiden Zahlen voneinander ab. Sind sie gleich, so kommt da als Ergebnis 0 heraus und &#039;&#039;&#039;cpi&#039;&#039;&#039; setzt daher konsequenter Weise das Zero-Flag, das anzeigt, daß die vorhergegangene Operation ein 0 Ergebnis hatte.&#039;&#039;&#039;breq&#039;&#039;&#039; wertet diese Status-Bits aus. Wenn die vorhergegangene Operation ein 0-Ergebnis hatte, das Zero-Flag also gesetzt ist, dann wird ein Sprung zum angegebenen Label durchgeführt. In Summe bewirkt also die Sequenz&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
das das gelesene Zeichen mit 0 verglichen wird und falls das gelesene&lt;br /&gt;
Zeichen tatsächlich 0 war, an der Stelle lcd_flash_string_2 weiter gemacht wird. Im anderen Fall wird die bereits geschriebene Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aufgerufen, welche das Zeichen ausgibt. &#039;&#039;&#039;lcd_data&#039;&#039;&#039; erwartet dabei das Zeichen im Register &#039;&#039;&#039;temp1&#039;&#039;&#039;, genau in dem Register in welches wir vorher mittels &#039;&#039;&#039;lpm&#039;&#039;&#039; das Zeichen geladen hatten.&lt;br /&gt;
&lt;br /&gt;
Das verwendende Programm sieht dann 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 temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
           rcall lcd_init              ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear             ; Display löschen&lt;br /&gt;
 &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;
&lt;br /&gt;
           rcall lcd_flash_string      ; Unterprogramm gibt String aus der&lt;br /&gt;
                                       ; durch den Z-Pointer adressiert wird&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
           .db &amp;quot;Test&amp;quot;,0                ; Stringkonstante, durch eine 0&lt;br /&gt;
                                       ; abgeschlossen  &lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD Funktionen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Genaueres über die Verwendung unterschiedlicher Speicher findet sich im Kapitel [[AVR-Tutorial:_Speicher|Speicher]]&lt;br /&gt;
&lt;br /&gt;
==Zahlen ausgeben==&lt;br /&gt;
Um Zahlen, die beispielsweise in einem Register gespeichert sind, ausgeben zu können, ist es notwendig sich eine Textrepräsentierung der Zahl zu generieren. Die Zahl 123 wird also in den Text &amp;quot;123&amp;quot; umgewandelt welcher dann ausgegeben wird. Aus praktischen Gründen wird allerdings der Text nicht vollständig generiert (man müsste ihn ja irgendwo zwischenspeichern) sondern die einzelnen Buchstaben werden sofort ausgegeben, sobald sie bekannt sind.&lt;br /&gt;
&lt;br /&gt;
===Dezimal ausgeben===&lt;br /&gt;
Das Prinzip der Umwandlung ist einfach. Um herauszufinden wieviele Hunderter in der Zahl 123 enthalten sind, genügt es in einer Schleife immer wieder 100 von der Zahl abzuziehen und mitzuzählen wie oft dies gelang, bevor das Ergebnis negativ wurde. In diesem Fall lautet die Antwort: 1 mal, denn 123 - 100 macht 23. Versucht man erneut 100 anzuziehen, so ergibt sich eine negative Zahl.&lt;br /&gt;
Also muss eine &#039;1&#039; ausgeben werden. Die verbleibenden 23 werden weiterbehandelt, indem festgestellt wird wieviele Zehner darin enthalten sind. Auch hier wiederrum: In einer Schleife solange 10 abziehen, bis das Ergebnis nagativ wurde. Konkret geht das 2 mal gut, also muss das nächste auszugebende Zeichen ein &#039;2&#039; sein. Damit verbleiben noch die Einer, welche direkt in das entsprechende Zeichen umgewandelt werden können. In Summe hat man also an das Display die Zeichen &#039;1&#039; &#039;2&#039; &#039;3&#039; ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number:&lt;br /&gt;
           push  temp2            ; die Funktion verändert temp2, also sichern&lt;br /&gt;
                                  ; wir den Inhalt, um ihn am Ende wieder&lt;br /&gt;
                                  ; herstellen zu können&lt;br /&gt;
&lt;br /&gt;
           mov   temp2, temp1     ; das Register temp1 frei machen&lt;br /&gt;
                                  ; abzählen wieviele Hunderter&lt;br /&gt;
                                  ; in der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
           subi  temp2, 100       ; 100 abziehen&lt;br /&gt;
           brcs  lcd_number_2     ; ist dadurch ein Unterlauf entstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Hunderter mehr ...&lt;br /&gt;
           rjmp  lcd_number_1     ; ... und ab zur nächsten Runde&lt;br /&gt;
;&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorherhgehende Schleife 100 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; abzählen wieviele Zehner in&lt;br /&gt;
                  ; der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_3:&lt;br /&gt;
           subi  temp2, 10        ; 10 abziehen&lt;br /&gt;
           brcs  lcd_number_4     ; ist dadurch ein Unterlauf enstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Zehner mehr ...&lt;br /&gt;
           rjmp  lcd_number_3     ; ... und ab zur nächsten Runde&lt;br /&gt;
&lt;br /&gt;
                              ; die Zehnerstelle ausgeben&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; die übrig gebliebenen Einer&lt;br /&gt;
                  ; noch ausgeben&lt;br /&gt;
           ldi   temp1, &#039;0&#039;       ; die Zahl in temp2 ist jetzt im Bereich&lt;br /&gt;
           add   temp1, temp2     ; 0 bis 9. Einfach nur den ASCII Code für&lt;br /&gt;
           rcall lcd_data         ; &#039;0&#039; dazu addieren und wir erhalten dierekt&lt;br /&gt;
                                  ; den ASCII Code für die Ziffer&lt;br /&gt;
&lt;br /&gt;
           pop   temp2            ; den gesicherten Inhalt von temp2 wieder herstellen&lt;br /&gt;
           ret                    ; und zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beachte: Diese Funktion benutzt wiederrum die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039;. Anders als bei den bisherigen Aufrufen ist &#039;&#039;&#039;lcd_number&#039;&#039;&#039; aber darauf angewiesen, dass &#039;&#039;&#039;lcd_data&#039;&#039;&#039; das Register &#039;&#039;&#039;temp2&#039;&#039;&#039; unangetastet lässt. Falls sie es noch nicht getan haben, dann ist das jetzt die perfekte Gelegenheit, &#039;&#039;&#039;lcd_data&#039;&#039;&#039; mit den entsprechenden &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; Befehlen zu versehen. Sie sollten dies unbedingt zur Übung selbst machen. Am Ende muß die Funktion dann wie diese hier aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1           ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap  temp1                  ; Vertauschen&lt;br /&gt;
           andi  temp1, 0b00001111      ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr   temp1, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp1        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi  temp2, 0b00001111      ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr   temp2, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp2        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur ohne RS zu setzen&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           andi  temp1, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi  temp2, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz zur Funktionsweise der Funktion &#039;&#039;&#039;lcd_number&#039;&#039;&#039;: Die Zahl in einem Register bewegt sich im Wertebereich 0 bis 255. Um herauszufinden, wie die Hunderterstelle lautet, zieht die Funktion einfach in einer Schleife immer wieder 100 von der Schleife ab, bis bei der Subtraktion ein Unterlauf, angezeigt durch das Setzen des Carry-Bits bei der Subtraktion, entsteht. Die Anzahl wird im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; mitgezählt. Da dieses Register mit dem ASCII Code von &#039;0&#039; initialisiert wurde, und dieser ASCII Code bei jedem Schleifendurchlauf um 1 erhöht wird, können wir das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; direkt zur Ausgabe des Zeichens für die Hunderterstelle durch die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; benutzen. Völlig analog funktioniert auch die Ausgabe der Zehnerstelle.&lt;br /&gt;
&lt;br /&gt;
===Unterdrückung von führenden Nullen===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung: Diese Routine ist fehlerhaft&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt jede Zahl im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; immer mit 3 Stellen aus. Führende Nullen werden nicht unterdrückt. Möchte man dies ändern, so ist das ganz leicht möglich: Vor Ausgabe der Hunderterstelle bzw. Zehnerstelle muss lediglich überprüft werden, ob die Entsprechende Ausgabe eine &#039;0&#039; wäre. Ist sie das, so wird die Ausgabe übersprungen. Lediglich in der Einerstelle wird jede Ziffer wie errechnet ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ...&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben, wenn&lt;br /&gt;
                                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_2a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_2a:&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&lt;br /&gt;
           ...&lt;br /&gt;
                              ; die Zehnerstelle ausgeben, wenn&lt;br /&gt;
                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_4a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_4a:&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Verfahren, die einzelnen Stellen durch Subtraktion zu bestimmen, ist bei kleinen Zahlen eine durchaus gängige Alternative. Vor allem dann, wenn keine hardwaremäßige Unterstützung für Multiplikation und Division zur Verfügung steht. Ansonsten könnte man die die einzelnen Ziffern auch durch Division bestimmen. Das Prinzip ist folgendes (beispielhaft an der Zahl 52783 gezeigt)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   52783 / 10          -&amp;gt; 5278&lt;br /&gt;
   52783 - 5278 * 10   -&amp;gt;          3&lt;br /&gt;
&lt;br /&gt;
   5278 / 10           -&amp;gt; 527&lt;br /&gt;
   5278 - 527 * 10     -&amp;gt;          8&lt;br /&gt;
&lt;br /&gt;
   527 / 10            -&amp;gt; 52&lt;br /&gt;
   527 - 52 * 10       -&amp;gt;          7&lt;br /&gt;
&lt;br /&gt;
   52 / 10             -&amp;gt; 5&lt;br /&gt;
   52 - 5 * 10         -&amp;gt;          2&lt;br /&gt;
&lt;br /&gt;
   5 / 10              -&amp;gt; 0&lt;br /&gt;
   5 - 0 * 10          -&amp;gt;          5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Prinzip ist also die Restbildung bei einer fortgesetzten Division durch 10, wobei die einzelnen Ziffern in umgekehrter Reihenfolge ihrer Wertigkeit entstehen. Dadurch hat man aber ein Problem: Damit die Zeichen in der richtigen Reihenfolge ausgegeben werden können, muß man sie meistens zwischenspeichern um sie in der richtigen Reihenfole ausgeben zu können. Wird die Zahl in einem Feld von immer gleicher Größe ausgegeben, dann kann man auch die Zahl von rechts nach links ausgeben (bei einem LCD ist das möglich).&lt;br /&gt;
&lt;br /&gt;
===Hexadezimal ausgeben===&lt;br /&gt;
&lt;br /&gt;
Zu guter letzt hier noch eine Funktion, die eine Zahl aus dem Register &#039;&#039;&#039;temp1&#039;&#039;&#039; in hexadezimaler Form ausgibt. Die Funktion weist keine Besonderheiten auf und sollte unmittelbar verständlich sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen hexadezimal ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number_hex:&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           rcall lcd_number_hex_digit&lt;br /&gt;
           swap  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_number_hex_digit:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
           andi  temp1, $0F&lt;br /&gt;
           cpi   temp1, 10&lt;br /&gt;
           brlt  lcd_number_hex_digit_1&lt;br /&gt;
           subi  temp1, -( &#039;A&#039; - &#039;9&#039; - 1 ) ; es wird subi mit negativer Konstante verwendet, weil es kein addi gibt&lt;br /&gt;
lcd_number_hex_digit_1:&lt;br /&gt;
           subi  temp1, -&#039;0&#039;               ; ditto&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           &lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Eine 16-Bit Zahl aus einem Registerpärchen ausgeben===&lt;br /&gt;
&lt;br /&gt;
Um eine 16 Bit Zahl auszugeben wird wieder das bewährte Schema benutzt die einzelnen Stellen durch Subtraktion abzuzählen. Da es sich hierbei allerdings um eine 16 Bit Zahl handelt, müssen die Subtraktionen als 16-Bit Arithmetik ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 16 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp2 (low Byte) / temp3 (high Byte)&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number16:&lt;br /&gt;
           push  temp1&lt;br /&gt;
           push  temp2&lt;br /&gt;
           push  temp3&lt;br /&gt;
&lt;br /&gt;
; die Zehntausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number0:&lt;br /&gt;
           subi  temp2, low(10000)&lt;br /&gt;
           sbci  temp3, high(10000)&lt;br /&gt;
           brcs  lcd_number1&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number0&lt;br /&gt;
&lt;br /&gt;
; .. und ausgeben&lt;br /&gt;
lcd_number1:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-10000)&lt;br /&gt;
           sbci  temp3, high(-10000)&lt;br /&gt;
&lt;br /&gt;
; die Tausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number2:&lt;br /&gt;
           subi  temp2, low(1000)&lt;br /&gt;
           sbci  temp3, high(1000)&lt;br /&gt;
           brcs  lcd_number3&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number2&lt;br /&gt;
&lt;br /&gt;
; ... und ausgeben&lt;br /&gt;
lcd_number3:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-1000)&lt;br /&gt;
           sbci  temp3, high(-1000)&lt;br /&gt;
&lt;br /&gt;
; Als nächtes kommt die Hunderterstelle drann&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number4:&lt;br /&gt;
           subi  temp2, low(100)&lt;br /&gt;
           sbci  temp3, high(100)&lt;br /&gt;
           brcs  lcd_number5&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number4&lt;br /&gt;
&lt;br /&gt;
; und ausgeben&lt;br /&gt;
lcd_number5:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100&lt;br /&gt;
&lt;br /&gt;
; bleiben noch die Zehner&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number6:&lt;br /&gt;
           subi  temp2, 10&lt;br /&gt;
           brcs  lcd_number7&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number6&lt;br /&gt;
&lt;br /&gt;
; ausgeben ...&lt;br /&gt;
lcd_number7:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10&lt;br /&gt;
&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
           add   temp1, temp2&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
; fertig. Stack wieder aufräumen&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           pop   temp3&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Der überarbeitete, komplette Code==&lt;br /&gt;
&lt;br /&gt;
Hier also die komplett überarbeitete Version der LCD Funktionen.&lt;br /&gt;
&lt;br /&gt;
Die für die Benutzung relevanten Funktionen&lt;br /&gt;
* &#039;&#039;&#039;lcd_init&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_clear&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_home&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_data&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_flash_string&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number_hex&#039;&#039;&#039;&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; übergeben, wobei &#039;&#039;&#039;temp1&#039;&#039;&#039; vom Usercode definiert werden muss.&lt;br /&gt;
&lt;br /&gt;
[[Media:lcd-routines.asm|Download lcd-routines.asm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Stack|&lt;br /&gt;
zurücklink=AVR-Tutorial: Stack|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Interrupts|&lt;br /&gt;
vorlink=AVR-Tutorial: Interrupts}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister SREG) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register temp1 übergeben, wobei temp1 vom Usercode definiert werden muss. &lt;br /&gt;
&lt;br /&gt;
der progger hat vergessen das Z reg in den sub&#039;s lcd_flash_string ...&lt;br /&gt;
zu sichern, aber danke für den Einstig&lt;br /&gt;
&lt;br /&gt;
Lars&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27993</id>
		<title>AVR-Tutorial: LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27993"/>
		<updated>2008-05-16T02:53:18Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Ansteuerung des LCDs im 4-Bit-Modus */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kaum ein elektronisches Gerät kommt heutzutage noch ohne ein LCD daher. Ist doch auch praktisch, Informationen im Klartext anzeigen zu können, ohne irgendwelche LEDs blinken zu lassen. Kein Wunder also, dass die häufigste Frage in Mikrocontroller-Foren ist: &amp;quot;Wie kann ich ein LCD anschließen?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-Bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-Bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-Bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-Bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-Bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-Bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4 Bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert. Ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitet hat. Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt, da wir wissen, welche Anschlüsse das LCD benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Pinnummer&amp;lt;BR&amp;gt;LCD || Bezeichnung || Anschluss&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |1 || Vss || GND&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |2 || Vcc || 5V&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |3 || Vee || GND oder [[Potentiometer | Poti]] &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |4 || RS || PD4 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |5 || RW || GND &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |6 || E || PD5 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |7 || DB0 || nicht angeschlossen &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |8 || DB1 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |9 || DB2 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |10 || DB3 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |11 || DB4 || PD0 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |12 || DB5 || PD1 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |13 || DB6 || PD2 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |14 || DB7 || PD3 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |15 || A || Vorsicht! Meistens nicht direkt an +5V anschließbar,&amp;lt;BR&amp;gt;nur über einen Vorwiderstand (z.B. 33&amp;amp;Omega;)!&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |16 || K || GND&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Ok alles ist verbunden. Wenn man jetzt den Strom einschaltet, sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display?&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung des LCDs im 4-Bit-Modus ==&lt;br /&gt;
&lt;br /&gt;
Um ein Byte zu übertragen, muss man es erstmal in die beiden Nibbles zerlegen, die getrennt übertragen werden. Da das obere Nibble (Bit 4 - Bit 7) als erstes übertragen wird, die 4 Datenleitungen jedoch an die vier unteren Bits des Port D angeschlossen sind, muss man die beiden Nibbles des zu übertragenden Bytes erstmal vertauschen. Der AVR kennt dazu praktischerweise einen eigenen Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           swap r16               ; vertauscht die beiden Nibbles von r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus 0b00100101 wird so z.B. 0b01010010. &lt;br /&gt;
&lt;br /&gt;
Jetzt sind die Bits für die erste Phase der Übertragung an der richtigen Stelle. Trotzdem wollen wir das Ergebnis nicht einfach so mit &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; an den Port geben. Um die Hälfte des Bytes, die jetzt nicht an die Datenleitungen des LCDs gegeben wird auf null zu setzen, verwendet man folgenden Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           andi r16, 0b00001111   ; Nur die vier unteren (mit 1 markierten)&lt;br /&gt;
                                  ; Bits werden übernommen, alle anderen werden null&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also: Das obere Nibble wird erst mit dem unteren vertauscht, damit es unten ist. Dann wird das obere (das wir jetzt noch nicht brauchen) auf null gesetzt. &lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem LCD noch mitteilen, ob wir Daten oder Befehle senden wollen. Das machen wir, indem wir das Bit, an dem RS angeschlossen ist (PD4), auf 0 (Befehl senden) oder auf 1 (Daten senden) setzen. Um ein Bit in einem normalen Register zu setzen, gibt es den Befehl sbr (Set Bit in Register). Dieser Befehl unterscheidet sich jedoch von sbi (das nur für IO-Register gilt) dadurch, dass man nicht die Nummer des zu setzenden Bits angibt, sondern eine Bitmaske. Das geht so: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbr r16, 0b00010000     ; Bit 4 setzen, alle anderen Bits bleiben gleich&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RS ist an PD4 angeschlossen. Wenn wir r16 an den Port D ausgeben, ist RS jetzt also high und das LCD erwartet Daten anstatt von Befehlen. &lt;br /&gt;
&lt;br /&gt;
Das Ergebnis können wir jetzt endlich direkt an den Port D übergeben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           out PORTD, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss vorher der Port D auf Ausgang geschalten werden, indem man 0xFF ins Datenrichtungsregister DDRD schreibt. &lt;br /&gt;
&lt;br /&gt;
Um dem LCD zu signalisieren, dass es das an den Datenleitungen anliegende Nibble übernehmen kann, wird die E-Leitung (Enable, an PD5 angeschlossen) auf high und kurz darauf wieder auf low gesetzt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbi PORTD, 5              ; Enable high&lt;br /&gt;
           nop                       ; 3 Taktzyklen warten (&amp;quot;nop&amp;quot; = nichts tun)&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5              ; Enable wieder low&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die eine Hälfte des Bytes wäre damit geschafft! Die andere Hälfte kommt direkt hinterher: Alles, was an der obenstehenden Vorgehensweise geändert werden muss, ist, das &amp;quot;swap&amp;quot; (Vertauschen der beiden Nibbles) wegzulassen.&lt;br /&gt;
&lt;br /&gt;
== Initialisierung des Displays ==&lt;br /&gt;
&lt;br /&gt;
Allerdings gibt es noch ein Problem. Wenn ein LCD eingeschaltet wird, dann läuft es zunächst im 8 Bit Modus. Irgendwie muss das Display initialisiert und auf den 4 Bit Modus umgeschaltet werden, und zwar nur mit den 4 zur Verfügung stehenden Datenleitungen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn es Probleme gibt, dann meistens an diesem Punkt. Die &amp;quot;kompatiblen&amp;quot; Kontroller sind gelegentlich doch nicht 100% identisch. Es lohnt sich das Datenblatt (siehe Weblinks im Artikel [[LCD]]) genau zu lesen, in welcher Reihenfolge und mit welchen Abständen (Delays) die Initialisierungbefehle gesendet werden. Eine weitere Hilfe können Ansteuerungsbeispiele in Forenbeiträgen geben z.B.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79609#664268 (A) KS0066U oder Ähnliche --- LCD Treiber]&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 4 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Achtung: Im folgenden sind alle Bytes aus Sicht des LCD-Kontrollers angegeben! Da LCD-seitig nur die Leitungen DB4 - DB7 verwendet werden, ist daher immer nur das höherwertige Nibbel gültig. Durch die Art der Verschaltung (DB4 - DB7 wurde auf dem PORT an PD0 bis PD3 angeschlossen) ergibt sich dadurch eine Verschiebung, so dass das am Kontroller auszugebende Byte nibblemässig vertauscht ist!&lt;br /&gt;
&lt;br /&gt;
Die Sequenz, aus Sicht des Kontrollers, sieht so aus:&lt;br /&gt;
&lt;br /&gt;
* nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 4.1ms warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 100µs warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* $2 ins Steuerregister schreiben (RS = 0), dadurch wird auf 4 Bit Daten umstellt&lt;br /&gt;
* ab jetzt muss für die Übertragung eines Bytes jeweils das höherwertige Nibble und dann das niederwertige Nibble übertragen werden, wie oben beschrieben&lt;br /&gt;
* Mit dem Konfigurier-Befehl $20 das Display konfigurieren (4-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 8 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Der Vollständigkeit halber hier noch die notwendige Initialiserungssequenz für den 8 Bit Modus. Da hier die Daten komplett als 1 Byte übertragen werden können, sind einige Klimmzüge wie im 4 Bit Modus nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
* nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 4.1ms warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 100µs warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mit dem Konfigurier-Befehl 0x30 das Display konfigurieren (8-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
== Routinen zur LCD-Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
Die Routinen zur Kommunikation mit dem LCD sehen also so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000 (Anm.1)&lt;br /&gt;
           out PORTD, temp1             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000&lt;br /&gt;
           out PORTD, temp2             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out PORTD, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
 ;&lt;br /&gt;
 ; Bei höherem Takt (&amp;gt;= 8 MHz) kann es notwendig sein, &lt;br /&gt;
 ; vor dem Enable High 1-2 Wartetakte (nop) einzufügen. &lt;br /&gt;
 ; Siehe dazu http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi PORTD, 5                 ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5                 ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
&lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi  temp3,50&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall  delay5ms&lt;br /&gt;
           dec  temp3&lt;br /&gt;
           brne powerupwait&lt;br /&gt;
           ldi temp1, 0b00000011        ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out PORTD, temp1             ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00000010        ; 4bit-Modus einstellen&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00101000        ; 4Bit / 2 Zeilen / 5x8&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00001100        ; Display ein / Cursor aus / kein Blinken&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00000100        ; inkrement / kein Scrollen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi temp1, 0b00000001   ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi temp1, 0b00000010   ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anm.1: Siehe [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
Weitere Funktionen (wie z.B. Cursorposition verändern) sollten mit Hilfe der [[AVR-Tutorial:_LCD#Welche_Befehle_versteht_das_LCD.3F|Befehlscodeliste]] nicht schwer zu realisieren sein. Einfach den Code in temp laden, lcd_command aufrufen und ggf. eine Pause einfügen. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man die LCD-Ansteuerung auch an einen anderen Port des Mikrocontrollers &amp;quot;verschieben&amp;quot;: Wenn das LCD z.B. an Port B angeschlossen ist, dann reicht es im Programm alle &amp;quot;PORTD&amp;quot; durch &amp;quot;PORTB&amp;quot; und &amp;quot;DDRD&amp;quot; durch &amp;quot;DDRB&amp;quot; zu ersetzen. &lt;br /&gt;
&lt;br /&gt;
Wer eine höhere Taktfrequenz als 4 MHz verwendet, der sollte daran denken die Dauer der Verzögerungsschleifen anzupassen.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das diese Routinen zur Anzeige von Text verwendet, kann z.B. so aussehen (die Datei lcd-routines.asm muss sich im gleichen Verzeichnis befinden). Nach der Initialisierung wird zuerst der Displayinhalt gelöscht. Um dem LCD ein Zeichen zu schicken, lädt man es in temp1 und ruft die Routine &amp;quot;lcd_data&amp;quot; auf. Das folgende Beispiel zeigt das Wort &amp;quot;Test&amp;quot; auf dem LCD an. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/lcd-test.asm Download lcd-test.asm] &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;
&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, 0xFF    ; Port D = Ausgang&lt;br /&gt;
           out DDRD, temp1&lt;br /&gt;
&lt;br /&gt;
           rcall lcd_init     ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear    ; Display löschen&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;T&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;e&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           &lt;br /&gt;
           ldi temp1, &#039;s&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;t&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD-Routinen werden hier eingefügt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für längere Texte ist die Methode, jedes Zeichen einzeln in das Register zu laden und &amp;quot;lcd_data&amp;quot; aufzurufen natürlich nicht sehr praktisch. Dazu später aber mehr.&lt;br /&gt;
&lt;br /&gt;
Bisher wurden in Register immer irgendwelche Zahlenwerte geladen, aber in diesem Programm kommt plötzlich die Anweisung&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
vor. Wie ist diese zu verstehen? Passiert hier etwas grundlegend anderes als beim Laden einer Zahl in ein Register?&lt;br /&gt;
&lt;br /&gt;
Die Antwort darauf lautet: Nein. Auch hier wird letztendlich nur eine Zahl in ein Register geladen. Der Schlüssel zum Verständnis beruht darauf, dass zum LCD, so wie zu allen Ausgabegeräten, für die Ausgabe von Texten immer nur Zahlen übertragen werden, sog. Codes. Zum Beispiel könnte man vereinbaren, dass ein LCD, wenn es den Ausgabecode 65 erhält ein &#039;A&#039; anzeigt, bei einem Ausgabecode von 66 ein &#039;B&#039; usw. Naturgemäß gibt es daher viele verschiedene Code-Buchstaben Zuordnungen. Damit hier etwas Ordnung in das potentielle Chaos kommt, hat man sich bereits in der Steinzeit der Programmierung auf bestimmte Codetabellen geeinigt, von denen die verbreitetste sicherlich die ASCII-Zuordnung ist.&lt;br /&gt;
&lt;br /&gt;
==ASCII==&lt;br /&gt;
&lt;br /&gt;
ASCII steht für &#039;&#039;American Standard Code for Information Interchange&#039;&#039; und ist ein standardisierter Code zu Zeichenumsetzung. Die Codetabelle sieht hexadezimal dabei wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;.0&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x1&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x2&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x3&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x4&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x6&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x7&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x8&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x9&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xA&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xB&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xC&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xD&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xE&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xF&amp;lt;/th&amp;gt;&amp;lt;br&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;&#039;&#039;&#039;0x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NUL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SOH&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;STX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EOT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ENQ&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ACK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BEL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;HT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;VT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CR&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SO&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SI&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;&#039;&#039;&#039;1x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DLE&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NAK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SYN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CAN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EM&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SUB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ESC&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;US&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;&#039;&#039;&#039;2x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SP&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;!&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;#&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;%&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;amp;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;(&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;)&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;*&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;+&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;,&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;.&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;3x&#039;&#039;&#039;&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;&amp;lt;td&amp;gt;3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;9&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;=&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;gt;&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;4x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;B&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;C&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;D&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;F&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;G&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;H&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;I&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;J&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;L&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;M&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;N&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;O&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;&#039;&#039;&#039;5x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;P&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;R&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;S&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;T&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;U&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;V&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;W&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;X&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;[&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;\&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;]&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;^&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;6x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;`&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;a&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;b&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;c&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;d&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;e&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;f&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;g&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;h&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;i&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;j&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;k&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;l&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;m&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;n&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;o&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;&#039;&#039;&#039;7x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;p&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;r&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;s&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;t&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;u&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;v&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;w&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;{&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;|&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;~&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DEL&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 ersten beiden Zeilen enthalten die Codes für einige Steuerzeichen, ihre vollständige Beschreibung würde hier zu weit führen. Das Zeichen &#039;&#039;&#039;SP&#039;&#039;&#039; steht für ein &#039;&#039;Space&#039;&#039;, also ein Leerzeichen. &#039;&#039;&#039;BS&#039;&#039;&#039; steht für &#039;&#039;Backspace&#039;&#039;, also ein Zeichen zurück. &#039;&#039;&#039;DEL&#039;&#039;&#039; steht für &#039;&#039;Delete&#039;&#039;, also das Löschen eines Zeichens. &#039;&#039;&#039;CR&#039;&#039;&#039; steht für &#039;&#039;Carriage Return&#039;&#039;, also wörtlich: der Wagenrücklauf (einer Schreibmaschine), während &#039;&#039;&#039;LF&#039;&#039;&#039; für &#039;&#039;Line feed&#039;&#039;, also einen Zeilenvorschub steht.&lt;br /&gt;
&lt;br /&gt;
Der Assembler kennt diese Codetabelle und ersetzt die Zeile&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, $54&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
was letztendlich auch der Lesbarkeit des Programmes zugute kommt. Funktional besteht kein Unterschied zwischen den beiden Anweisungen. Beide bewirken, dass das Register temp1 mit dem Bitmuster 01010100 ( = hexadezimal 54, = dezimal 84 oder eben der ASCII Code für &#039;&#039;&#039;T&#039;&#039;&#039;) geladen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das LCD wiederrum kennt diese Code-Tabelle ebenfalls und wenn es über den Datenbus die Codezahl $54 zur Anzeige empfängt, dann schreibt es ein &#039;&#039;&#039;T&#039;&#039;&#039; an die aktuelle Cursorposition. Genauer gesagt, weiss das LCD nichts von einem &#039;&#039;&#039;T&#039;&#039;&#039;. Es sieht einfach in seinen internen Tabellen nach, welche Pixel beim Empfang der Codezahl $54 auf schwarz zu setzen sind. &#039;Zufällig&#039; sind das genau jene Pixel, die für uns Menschen ein &#039;&#039;&#039;T&#039;&#039;&#039; ergeben.&lt;br /&gt;
&lt;br /&gt;
==Welche Befehle versteht das LCD?==&lt;br /&gt;
&lt;br /&gt;
Auf dem LCD arbeitet ein Kontroller vom Typ HD44780. Diesen Kontroller versteht eine Reihe von Befehlen, die allesamt mittels lcd_command gesendet werden können. Ein Kommando ist dabei nichts anderes als ein Befehlsbyte, indem die verschiedenen Bits verschiedene Bedeutung besitzen:&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 0 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 1 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;der Zustand dieses Bits ist egal&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;sonstige Buchstaben&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;das Bit muss je nach gewünschter Funktionalität gesetzt werden. Die mögliche Funktionalität des jeweiligen Bits geht aus der Befehlsbeschreibung hervor&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Kommando &#039;ON/OFF Control&#039; soll benutzt werden um das Display einzuschalten, der Cursor soll eingeschaltet werden und der Cursor soll blinken.&lt;br /&gt;
Das Befehlsbyte ist so aufgebaut:&lt;br /&gt;
   0b00001dcb&lt;br /&gt;
Aus der Befehlsbeschreibung entnimmt man:&lt;br /&gt;
* Display ein bedeutet, dass an der Bitposition d eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor ein bedeutet, dass an der Bitposition c ein 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor blinken bedeutet, dass an der Bitposition b eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
Das dafür zu übertragende Befehlsbyte hat also die Gestalt 0b00001111 oder in hexadezimaler Schreibweise $0F&lt;br /&gt;
&lt;br /&gt;
===Clear display: 0b00000001===&lt;br /&gt;
&lt;br /&gt;
Die Anzeige wird gelöscht und der Ausgabecursor kehrt an die Home Position (links, erste Zeile) zurück&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Cursor home: 0b0000001x===&lt;br /&gt;
&lt;br /&gt;
Der Cursor kehrt an die Home Position (links, erste Zeile) zurück. Ein verschobenes Display wird auf die Grundeinstellung zurückgesetzt.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs bis 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Entry mode: 0b000001is===&lt;br /&gt;
&lt;br /&gt;
Legt die Cursor Richtung fest sowie eine mögliche Verschiebung des Displays fest&lt;br /&gt;
* i = 1, Cursorposition bei Ausgabe eines Zeichens erhöhen&lt;br /&gt;
* i = 0, Cursorposition bei Ausgabe eines Zeichens vermindern&lt;br /&gt;
* s = 1, Display wird gescrollt, wenn der Cursor das Ende/Anfang, je nach Einstellung von i, erreicht hat.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===On/off control: 0b00001dcb===&lt;br /&gt;
&lt;br /&gt;
Display insgesamt ein/ausschalten; den Cursor ein/ausschalten; den Cursor auf blinken schalten/blinken aus. Wenn das Display ausgeschaltet wird, geht der Inhalt des Displays nicht verloren. Der vorher angezeigte Text wird nach wiedereinschalten erneut angezeigt.&lt;br /&gt;
Ist der Cursor eingeschaltet, aber Blinken ausgeschaltet, so wird der Cursor als Cursorzeile in Pixelzeile 8 dargestellt. Ist Blinken eingeschaltet, wird der Cursor als blinkendes ausgefülltes Rechteck dargestellt, welches abwechselnd mit dem Buchstaben an dieser Stelle angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
* d = 0, Display aus&lt;br /&gt;
* d = 1, Display ein&lt;br /&gt;
* c = 0, Cursor aus&lt;br /&gt;
* c = 1, Cursor ein&lt;br /&gt;
* b = 0, Cursor blinken aus&lt;br /&gt;
* b = 1, Cursor blinken ein&lt;br /&gt;
 &lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Cursor/Scrollen: 0b0001srxx===&lt;br /&gt;
&lt;br /&gt;
Bewegt den Cursor oder scrollt das Display um eine Position entweder nach rechts oder nach links.&lt;br /&gt;
&lt;br /&gt;
* s = 1, Display scrollen&lt;br /&gt;
* s = 0, Cursor bewegen&lt;br /&gt;
* r = 1, nach rechts&lt;br /&gt;
* r = 0, nach links &lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Konfiguration: 0b001dnfxx===&lt;br /&gt;
&lt;br /&gt;
Einstellen der Interface Art, Modus, Font&lt;br /&gt;
* d = 0, 4-Bit Interface&lt;br /&gt;
* d = 1, 8-Bit Interface&lt;br /&gt;
* n = 0, 1 zeilig&lt;br /&gt;
* n = 1, 2 zeilig&lt;br /&gt;
* f = 0, 5x7 Pixel&lt;br /&gt;
* f = 1, 5x11 Pixel&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Character RAM Address Set: 0b01aaaaaa===&lt;br /&gt;
&lt;br /&gt;
Mit diesem Kommando werden maximal 8 selbst definierte Zeichen definiert. Dazu wird der Character RAM Zeiger auf den Anfang des Character Generator (CG) RAM gesetzt und das Zeichen durch die Ausgabe von 8 Byte definiert. Der Adress Zeiger wird nach Ausgabe jeder Pixelzeile (8Bit) vom LCD selbst erhöht. Nach Beendigung der Zeichendefinition muss die Schreibposition explizit mit dem Kommando &amp;quot;Display RAM Address Set&amp;quot; wieder in den DD-RAM Bereich gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
aaaaaa 6-bit CG RAM Adresse&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Display RAM Address Set: 0b1aaaaaaa===&lt;br /&gt;
&lt;br /&gt;
Den Cursor neu positionieren. Display Data (DD) Ram ist vom Character Generator (CG) Ram unabhängig. Der Adresszeiger wird bei Ausgabe eines Zeichens ins DD Ram automatisch erhöht. Das Display verhält sich so, als ob eine Zeile immer aus 32 logischen Zeichen besteht, von der, je nach konkretem Displaytyp (16 Zeichen, 20 Zeichen) immer nur ein Teil sichtbar ist.&lt;br /&gt;
&lt;br /&gt;
aaaaaaa 7-bit DD RAM Adresse. Auf 2-zeiligen Displays (und den meisten 16x1 Displays), kann die Adressangabe wie folgt interpretiert werden&lt;br /&gt;
&lt;br /&gt;
1laaaaaa&lt;br /&gt;
* l = Zeilennummer (0 oder 1)&lt;br /&gt;
* a = 6-Bit Spaltennummer&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
==Einschub: Code aufräumen==&lt;br /&gt;
&lt;br /&gt;
Es wird Zeit sich einmal etwas kritisch mit den bisher geschriebenen Funktionen auseinander zu setzen.&lt;br /&gt;
&lt;br /&gt;
===Portnamen aus dem Code herausziehen===&lt;br /&gt;
&lt;br /&gt;
Wenn wir die LCD-Funktionen einmal genauer betrachten, dann fällt sofort auf, daß über die Funktionen verstreut immer wieder das &#039;&#039;&#039;PORTD&#039;&#039;&#039; sowie einzelne Zahlen für die Pins an diesem Port auftauchen. Wenn das LCD an einem anderen Port betrieben werden soll, oder sich die Pin-Belegung ändert, dann muß an all diesen Stellen eine Anpassung vorgenommen werden. Dabei darf keine einzige Stelle übersehen werden, ansonsten würden die LCD-Funktionen nicht oder nicht vollständig funktionieren.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit dem vorzubeugen ist es, diese immer gleichbleibenden Dinge an den Anfang der LCD-Funktionen vorzuziehen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
.equ PIN_E    = 5&lt;br /&gt;
.equ PIN_RS   = 4&lt;br /&gt;
&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp1          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp2          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi LCD_PORT, PIN_E          ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi LCD_PORT, PIN_E          ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
 &lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf Ausgang&lt;br /&gt;
           out   LCD_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi   temp3,6&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           dec   temp3&lt;br /&gt;
           brne  powerupwait&lt;br /&gt;
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out   LCD_PORT, temp1        ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00101000      ; 4 Bot, 2 Zeilen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00001100      ; Display on, Cursor off&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00000100      ; endlich fertig&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi   temp1, 0b00000010      ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels &#039;&#039;&#039;.equ&#039;&#039;&#039; werden mit dem Assembler Textersetzungen vereinbart. Der Assembler ersetzt alle Vorkomnisse des Quelltextes durch den zu ersetzenden Text. Dadurch ist es zb. möglich alle Vorkommnisse von &#039;&#039;&#039;PORTD&#039;&#039;&#039; durch &#039;&#039;&#039;LCD_PORT&#039;&#039;&#039; auszutauschen. Wird das LCD an einen anderen Port, zb. &#039;&#039;&#039;PORTB&#039;&#039;&#039; gelegt, dann genügt es, einzig und alleine die Zeilen&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTB&lt;br /&gt;
.equ LCD_DDR  = DDRB&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu ersetzen. Der Assembler sorgt dann dafür, dass diese Portänderung an den relevanten Stellen im Code über die Textersetzungen einfliesst. Selbiges natürlich mit der Pin-Zuordnung.&lt;br /&gt;
&lt;br /&gt;
===Registerbenutzung===&lt;br /&gt;
&lt;br /&gt;
Bei diesen Funktionen mussten einige Register des Prozessors benutzt werden um darin Zwischenergebnisse zu speichern bzw. zu bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss dabei natürlich, dass es zu keinen Überschneidungen kommt. Solange nur jede Funktion jeweils für sich betrachtet wird, ist das kein Problem. In 20 oder 30 Code-Zeilen kann man gut verfolgen, welches Register wofür benutzt wird. Schwieriger wird es, wenn Funktionen wiederrum andere Funktionen aufrufen, die ihrerseits wieder Funktionen aufrufen usw. Jede dieser Funktionen benutzt einige Register und mit zunehmender Programmgröße wird es immer schwieriger zu verfolgen, welches Register zu welchem Zeitpunkt wofür benutzt wird.&lt;br /&gt;
&lt;br /&gt;
Speziell bei Basisfunktionen, wie diesen LCD-Funktionen, ist es daher oft ratsam, dafür zu sorgen, daß jede Funktion die Register wieder in dem Zustand hinterlässt, indem sie sie auch vorgefunden hat. Wir benötigen dazu wieder den Stack, auf dem die Registerinhalte bei Betreten einer Funktion zwischengespeichert werden und von dem die Register bei Verlassen einer Funktion wiederhergestellt werden.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir die Funktion&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion verändert das Register temp1. Um das Register abzusichern schreiben wir die Funktion um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           push  temp1                  ; temp1 auf dem Stack sichern&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           pop   temp1                  ; temp1 vom Stack wiederherstellen&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am besten hält man sich an die Regel: Jede Funktion ist dafür zuständig die Register zu sichern und wiederherzustellen, die sie auch selbst verändert. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; ruft die Funktionen &#039;&#039;&#039;lcd_command&#039;&#039;&#039; und &#039;&#039;&#039;delay5ms&#039;&#039;&#039; auf. Wenn diese Funktionen selbst wieder Register verändern (und das tun sie), so ist es die Aufgabe dieser Funktionen, sich um die Sicherung und das Wiederherstellen der entsprechenden Register zu kümmern. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; sollte sich nicht darum kümmern müssen. Auf diese Weise ist das Schlimmste, das einem passieren kann, das ein paar Register unnütz gesichert und wiederhergestellt werden. Das kostet zwar etwas Rechenzeit und etwas Speicherplatz auf dem Stack, ist aber immer noch besser als das andere Extrem: Nach einem Funktionsaufruf haben einige Register nicht mehr den Wert den sie haben sollten und das Programm rechnet mit falschen Zahlen weiter.&lt;br /&gt;
&lt;br /&gt;
===Lass den Assembler rechnen===&lt;br /&gt;
Betrachtet man den Code genauer, so fallen einige konstante Zahlenwerte auf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Das vorangestellte $ kennzeichnet die Zahl als Hexadezimalzahl) Der Code benötigt eine Warteschleife die mindestens 50µs dauert. Die beiden Befehle innerhalb der Schleife benötigen 3 Takte: 1 Takt für den &#039;&#039;&#039;dec&#039;&#039;&#039; und der &#039;&#039;&#039;brne&#039;&#039;&#039; benötigt 2 Takte wenn die Bedingung zutrifft, der Branch also genommen wird. Bei 4 Mhz werden also 4000000 / 3 * 50 / 1000000 = 66.6 Durchläufe durch die Schleife benötigt um eine Verzögerungszeit von 50µs (0.000050 Sekunden) zu erreichen, hexadezimal ausgedrückt: $42&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist: Bei anderen Taktfrequenzen müsste man nun jedesmal diese Berechnung machen und den entsprechenden Zahlenwert einsetzen. Das kann aber der Assembler genausogut erledigen. Am Anfang des Codes wird ein Eintrag definiert, der die Taktfrequenz festlegt. Traditionell heist dieser Eintrag &amp;lt;i&amp;gt;XTAL&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ XTAL  = 4000000&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 50 / 3 ) / 1000000&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An einer anderen Codestelle gibt es weitere derartige magische Zahlen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was geht hier vor?&lt;br /&gt;
Die innere Schleife benötigt wieder 3 Takte pro Durchlauf. Bei $C9 = 201 Durchläufen werden also 201 * 3 = 603 Takte verbraucht. In der äußeren Schleife kommen pro Durchlauf alo 1 + 603 + 1 + 2 = 607 Takte verbraucht. Da die äußere Schleife $21 = 33 mal wiederholt wird, werden 20031 Takte verbraucht. Bei 4Mhz benötigt der Prozessor 20031 / 4000000 = 0.005007 Sekunden, also 5 ms.&lt;br /&gt;
Wird der Wiederholwert für die innere Schleife bei $C9 belassen, so werden 4000000 / 607 * 5 / 1000 Wiederholungen der äusseren Schleife benötigt. Auch diese Berechnung kann wieder der Assembler übernehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 5 / 607 ) / 1000&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein kleines Problem kann bei der Verwendung dieses Verfahrens entstehen: Bei hohen Taktfrequenzen und großen Wartezeiten kann der berechnete Wert größer als 255 werden und man bekommt die Fehlermeldung &amp;quot;Operand(s) out of range&amp;quot; beim Assemblieren. Dieser Fall tritt zum Beispiel für obige Konstruktion bei einer Taktfrequenz von 16 MHz ein (genauer gesagt ab 15,3 MHz), während darunter XTAL beliebig geändert werden kann. Als einfachste Lösung bietet es sich an, die Zahl der Takte pro Schleifendurchlauf durch das Einfügen von &#039;&#039;&#039;nop&#039;&#039;&#039; zu erhöhen und die Berechnungsvorschrift anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Ausgabe eines konstanten Textes ==&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde schon einmal ein Text ausgegeben. Dies geschah durch Ausgabe von einzelnen Zeichen. Das können wir auch anders machen. Wir können den Text im Speicher ablegen und eine Funktion schreiben, die die einzelnen Zeichen aus dem Speicher holt und ausgibt. Dabei erhebt sich aber eine Fragestellung: Woher weiß die Funktion eigentlich, wie lange der Text ist? Die Antwort darauf lautet: Sie kann es nicht wissen. Wir müssen irgendwelche Vereinbarungen treffen, woran die Funktion erkennen kann, dass der Text zu Ende ist. Im Wesentlichen werden dazu 2 Methoden benutzt:&lt;br /&gt;
* Der Text enthält ein spezielles Zeichen, welches das Ende des Textes markiert&lt;br /&gt;
* Wir speichern nicht nur den Text selbst, sondern auch die Länge des Textes&lt;br /&gt;
Mit einer der beiden Methoden ist es der Textausgabefunktion dann ein Leichtes, den Text vollständig auszugeben.&lt;br /&gt;
&lt;br /&gt;
Wir werden uns im Weiteren dafür entscheiden, ein spezielles Zeichen, eine 0, dafür zu benutzen. Die Ausgabefunktionen werden dann etwas einfacher, als wenn bei der Ausgabe die Anzahl der bereits ausgegebenen Zeichen mitgezählt werden muss.&lt;br /&gt;
&lt;br /&gt;
Den Text selbst speichern wir im Flash-Speicher, also dort wo auch das Programm gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Einen konstanten Text aus dem Flash Speicher&lt;br /&gt;
 ; ausgeben. Der Text wird mit einer 0 beendet&lt;br /&gt;
lcd_flash_string:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_1:&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           rjmp  lcd_flash_string_1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_2:&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion benutzt den Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039; um das jeweils nächste Zeichen aus dem Flash Speicher in ein Register zur Weiterverarbeitung zu laden. Dazu wird der sog. &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; benutzt. So nennt man das Registerpaar &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;. Nach jedem Ladevorgang wird dabei durch den Befehl&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
dieser Z-Pointer um 1 erhöht. Mittels &#039;&#039;&#039;cpi&#039;&#039;&#039; wird das in das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; geladene Zeichen mit 0 verglichen. &#039;&#039;&#039;cpi&#039;&#039;&#039; vergleicht die beiden Zahlen und merkt sich das Ergebnis in einem speziellen Register in Form von Status Bits. &#039;&#039;&#039;cpi&#039;&#039;&#039; zieht dabei ganz einfach die beiden Zahlen voneinander ab. Sind sie gleich, so kommt da als Ergebnis 0 heraus und &#039;&#039;&#039;cpi&#039;&#039;&#039; setzt daher konsequenter Weise das Zero-Flag, das anzeigt, daß die vorhergegangene Operation ein 0 Ergebnis hatte.&#039;&#039;&#039;breq&#039;&#039;&#039; wertet diese Status-Bits aus. Wenn die vorhergegangene Operation ein 0-Ergebnis hatte, das Zero-Flag also gesetzt ist, dann wird ein Sprung zum angegebenen Label durchgeführt. In Summe bewirkt also die Sequenz&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
das das gelesene Zeichen mit 0 verglichen wird und falls das gelesene&lt;br /&gt;
Zeichen tatsächlich 0 war, an der Stelle lcd_flash_string_2 weiter gemacht wird. Im anderen Fall wird die bereits geschriebene Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aufgerufen, welche das Zeichen ausgibt. &#039;&#039;&#039;lcd_data&#039;&#039;&#039; erwartet dabei das Zeichen im Register &#039;&#039;&#039;temp1&#039;&#039;&#039;, genau in dem Register in welches wir vorher mittels &#039;&#039;&#039;lpm&#039;&#039;&#039; das Zeichen geladen hatten.&lt;br /&gt;
&lt;br /&gt;
Das verwendende Programm sieht dann 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 temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
           rcall lcd_init              ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear             ; Display löschen&lt;br /&gt;
 &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;
&lt;br /&gt;
           rcall lcd_flash_string      ; Unterprogramm gibt String aus der&lt;br /&gt;
                                       ; durch den Z-Pointer adressiert wird&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
           .db &amp;quot;Test&amp;quot;,0                ; Stringkonstante, durch eine 0&lt;br /&gt;
                                       ; abgeschlossen  &lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD Funktionen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Genaueres über die Verwendung unterschiedlicher Speicher findet sich im Kapitel [[AVR-Tutorial:_Speicher|Speicher]]&lt;br /&gt;
&lt;br /&gt;
==Zahlen ausgeben==&lt;br /&gt;
Um Zahlen, die beispielsweise in einem Register gespeichert sind, ausgeben zu können, ist es notwendig sich eine Textrepräsentierung der Zahl zu generieren. Die Zahl 123 wird also in den Text &amp;quot;123&amp;quot; umgewandelt welcher dann ausgegeben wird. Aus praktischen Gründen wird allerdings der Text nicht vollständig generiert (man müsste ihn ja irgendwo zwischenspeichern) sondern die einzelnen Buchstaben werden sofort ausgegeben, sobald sie bekannt sind.&lt;br /&gt;
&lt;br /&gt;
===Dezimal ausgeben===&lt;br /&gt;
Das Prinzip der Umwandlung ist einfach. Um herauszufinden wieviele Hunderter in der Zahl 123 enthalten sind, genügt es in einer Schleife immer wieder 100 von der Zahl abzuziehen und mitzuzählen wie oft dies gelang, bevor das Ergebnis negativ wurde. In diesem Fall lautet die Antwort: 1 mal, denn 123 - 100 macht 23. Versucht man erneut 100 anzuziehen, so ergibt sich eine negative Zahl.&lt;br /&gt;
Also muss eine &#039;1&#039; ausgeben werden. Die verbleibenden 23 werden weiterbehandelt, indem festgestellt wird wieviele Zehner darin enthalten sind. Auch hier wiederrum: In einer Schleife solange 10 abziehen, bis das Ergebnis nagativ wurde. Konkret geht das 2 mal gut, also muss das nächste auszugebende Zeichen ein &#039;2&#039; sein. Damit verbleiben noch die Einer, welche direkt in das entsprechende Zeichen umgewandelt werden können. In Summe hat man also an das Display die Zeichen &#039;1&#039; &#039;2&#039; &#039;3&#039; ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number:&lt;br /&gt;
           push  temp2            ; die Funktion verändert temp2, also sichern&lt;br /&gt;
                                  ; wir den Inhalt, um ihn am Ende wieder&lt;br /&gt;
                                  ; herstellen zu können&lt;br /&gt;
&lt;br /&gt;
           mov   temp2, temp1     ; das Register temp1 frei machen&lt;br /&gt;
                                  ; abzählen wieviele Hunderter&lt;br /&gt;
                                  ; in der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
           subi  temp2, 100       ; 100 abziehen&lt;br /&gt;
           brcs  lcd_number_2     ; ist dadurch ein Unterlauf entstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Hunderter mehr ...&lt;br /&gt;
           rjmp  lcd_number_1     ; ... und ab zur nächsten Runde&lt;br /&gt;
;&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorherhgehende Schleife 100 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; abzählen wieviele Zehner in&lt;br /&gt;
                  ; der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_3:&lt;br /&gt;
           subi  temp2, 10        ; 10 abziehen&lt;br /&gt;
           brcs  lcd_number_4     ; ist dadurch ein Unterlauf enstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Zehner mehr ...&lt;br /&gt;
           rjmp  lcd_number_3     ; ... und ab zur nächsten Runde&lt;br /&gt;
&lt;br /&gt;
                              ; die Zehnerstelle ausgeben&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; die übrig gebliebenen Einer&lt;br /&gt;
                  ; noch ausgeben&lt;br /&gt;
           ldi   temp1, &#039;0&#039;       ; die Zahl in temp2 ist jetzt im Bereich&lt;br /&gt;
           add   temp1, temp2     ; 0 bis 9. Einfach nur den ASCII Code für&lt;br /&gt;
           rcall lcd_data         ; &#039;0&#039; dazu addieren und wir erhalten dierekt&lt;br /&gt;
                                  ; den ASCII Code für die Ziffer&lt;br /&gt;
&lt;br /&gt;
           pop   temp2            ; den gesicherten Inhalt von temp2 wieder herstellen&lt;br /&gt;
           ret                    ; und zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beachte: Diese Funktion benutzt wiederrum die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039;. Anders als bei den bisherigen Aufrufen ist &#039;&#039;&#039;lcd_number&#039;&#039;&#039; aber darauf angewiesen, dass &#039;&#039;&#039;lcd_data&#039;&#039;&#039; das Register &#039;&#039;&#039;temp2&#039;&#039;&#039; unangetastet lässt. Falls sie es noch nicht getan haben, dann ist das jetzt die perfekte Gelegenheit, &#039;&#039;&#039;lcd_data&#039;&#039;&#039; mit den entsprechenden &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; Befehlen zu versehen. Sie sollten dies unbedingt zur Übung selbst machen. Am Ende muß die Funktion dann wie diese hier aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1           ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap  temp1                  ; Vertauschen&lt;br /&gt;
           andi  temp1, 0b00001111      ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr   temp1, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp1        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi  temp2, 0b00001111      ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr   temp2, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp2        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur ohne RS zu setzen&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           andi  temp1, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi  temp2, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz zur Funktionsweise der Funktion &#039;&#039;&#039;lcd_number&#039;&#039;&#039;: Die Zahl in einem Register bewegt sich im Wertebereich 0 bis 255. Um herauszufinden, wie die Hunderterstelle lautet, zieht die Funktion einfach in einer Schleife immer wieder 100 von der Schleife ab, bis bei der Subtraktion ein Unterlauf, angezeigt durch das Setzen des Carry-Bits bei der Subtraktion, entsteht. Die Anzahl wird im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; mitgezählt. Da dieses Register mit dem ASCII Code von &#039;0&#039; initialisiert wurde, und dieser ASCII Code bei jedem Schleifendurchlauf um 1 erhöht wird, können wir das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; direkt zur Ausgabe des Zeichens für die Hunderterstelle durch die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; benutzen. Völlig analog funktioniert auch die Ausgabe der Zehnerstelle.&lt;br /&gt;
&lt;br /&gt;
===Unterdrückung von führenden Nullen===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung: Diese Routine ist fehlerhaft&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt jede Zahl im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; immer mit 3 Stellen aus. Führende Nullen werden nicht unterdrückt. Möchte man dies ändern, so ist das ganz leicht möglich: Vor Ausgabe der Hunderterstelle bzw. Zehnerstelle muss lediglich überprüft werden, ob die Entsprechende Ausgabe eine &#039;0&#039; wäre. Ist sie das, so wird die Ausgabe übersprungen. Lediglich in der Einerstelle wird jede Ziffer wie errechnet ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ...&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben, wenn&lt;br /&gt;
                                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_2a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_2a:&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&lt;br /&gt;
           ...&lt;br /&gt;
                              ; die Zehnerstelle ausgeben, wenn&lt;br /&gt;
                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_4a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_4a:&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Verfahren, die einzelnen Stellen durch Subtraktion zu bestimmen, ist bei kleinen Zahlen eine durchaus gängige Alternative. Vor allem dann, wenn keine hardwaremäßige Unterstützung für Multiplikation und Division zur Verfügung steht. Ansonsten könnte man die die einzelnen Ziffern auch durch Division bestimmen. Das Prinzip ist folgendes (beispielhaft an der Zahl 52783 gezeigt)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   52783 / 10          -&amp;gt; 5278&lt;br /&gt;
   52783 - 5278 * 10   -&amp;gt;          3&lt;br /&gt;
&lt;br /&gt;
   5278 / 10           -&amp;gt; 527&lt;br /&gt;
   5278 - 527 * 10     -&amp;gt;          8&lt;br /&gt;
&lt;br /&gt;
   527 / 10            -&amp;gt; 52&lt;br /&gt;
   527 - 52 * 10       -&amp;gt;          7&lt;br /&gt;
&lt;br /&gt;
   52 / 10             -&amp;gt; 5&lt;br /&gt;
   52 - 5 * 10         -&amp;gt;          2&lt;br /&gt;
&lt;br /&gt;
   5 / 10              -&amp;gt; 0&lt;br /&gt;
   5 - 0 * 10          -&amp;gt;          5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Prinzip ist also die Restbildung bei einer fortgesetzten Division durch 10, wobei die einzelnen Ziffern in umgekehrter Reihenfolge ihrer Wertigkeit entstehen. Dadurch hat man aber ein Problem: Damit die Zeichen in der richtigen Reihenfolge ausgegeben werden können, muß man sie meistens zwischenspeichern um sie in der richtigen Reihenfole ausgeben zu können. Wird die Zahl in einem Feld von immer gleicher Größe ausgegeben, dann kann man auch die Zahl von rechts nach links ausgeben (bei einem LCD ist das möglich).&lt;br /&gt;
&lt;br /&gt;
===Hexadezimal ausgeben===&lt;br /&gt;
&lt;br /&gt;
Zu guter letzt hier noch eine Funktion, die eine Zahl aus dem Register &#039;&#039;&#039;temp1&#039;&#039;&#039; in hexadezimaler Form ausgibt. Die Funktion weist keine Besonderheiten auf und sollte unmittelbar verständlich sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen hexadezimal ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number_hex:&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           rcall lcd_number_hex_digit&lt;br /&gt;
           swap  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_number_hex_digit:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
           andi  temp1, $0F&lt;br /&gt;
           cpi   temp1, 10&lt;br /&gt;
           brlt  lcd_number_hex_digit_1&lt;br /&gt;
           subi  temp1, -( &#039;A&#039; - &#039;9&#039; - 1 ) ; es wird subi mit negativer Konstante verwendet, weil es kein addi gibt&lt;br /&gt;
lcd_number_hex_digit_1:&lt;br /&gt;
           subi  temp1, -&#039;0&#039;               ; ditto&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           &lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Eine 16-Bit Zahl aus einem Registerpärchen ausgeben===&lt;br /&gt;
&lt;br /&gt;
Um eine 16 Bit Zahl auszugeben wird wieder das bewährte Schema benutzt die einzelnen Stellen durch Subtraktion abzuzählen. Da es sich hierbei allerdings um eine 16 Bit Zahl handelt, müssen die Subtraktionen als 16-Bit Arithmetik ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 16 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp2 (low Byte) / temp3 (high Byte)&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number16:&lt;br /&gt;
           push  temp1&lt;br /&gt;
           push  temp2&lt;br /&gt;
           push  temp3&lt;br /&gt;
&lt;br /&gt;
; die Zehntausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number0:&lt;br /&gt;
           subi  temp2, low(10000)&lt;br /&gt;
           sbci  temp3, high(10000)&lt;br /&gt;
           brcs  lcd_number1&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number0&lt;br /&gt;
&lt;br /&gt;
; .. und ausgeben&lt;br /&gt;
lcd_number1:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-10000)&lt;br /&gt;
           sbci  temp3, high(-10000)&lt;br /&gt;
&lt;br /&gt;
; die Tausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number2:&lt;br /&gt;
           subi  temp2, low(1000)&lt;br /&gt;
           sbci  temp3, high(1000)&lt;br /&gt;
           brcs  lcd_number3&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number2&lt;br /&gt;
&lt;br /&gt;
; ... und ausgeben&lt;br /&gt;
lcd_number3:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-1000)&lt;br /&gt;
           sbci  temp3, high(-1000)&lt;br /&gt;
&lt;br /&gt;
; Als nächtes kommt die Hunderterstelle drann&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number4:&lt;br /&gt;
           subi  temp2, low(100)&lt;br /&gt;
           sbci  temp3, high(100)&lt;br /&gt;
           brcs  lcd_number5&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number4&lt;br /&gt;
&lt;br /&gt;
; und ausgeben&lt;br /&gt;
lcd_number5:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100&lt;br /&gt;
&lt;br /&gt;
; bleiben noch die Zehner&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number6:&lt;br /&gt;
           subi  temp2, 10&lt;br /&gt;
           brcs  lcd_number7&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number6&lt;br /&gt;
&lt;br /&gt;
; ausgeben ...&lt;br /&gt;
lcd_number7:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10&lt;br /&gt;
&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
           add   temp1, temp2&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
; fertig. Stack wieder aufräumen&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           pop   temp3&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Der überarbeitete, komplette Code==&lt;br /&gt;
&lt;br /&gt;
Hier also die komplett überarbeitete Version der LCD Funktionen.&lt;br /&gt;
&lt;br /&gt;
Die für die Benutzung relevanten Funktionen&lt;br /&gt;
* &#039;&#039;&#039;lcd_init&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_clear&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_home&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_data&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_flash_string&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number_hex&#039;&#039;&#039;&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; übergeben, wobei &#039;&#039;&#039;temp1&#039;&#039;&#039; vom Usercode definiert werden muss.&lt;br /&gt;
&lt;br /&gt;
[[Media:lcd-routines.asm|Download lcd-routines.asm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Stack|&lt;br /&gt;
zurücklink=AVR-Tutorial: Stack|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Interrupts|&lt;br /&gt;
vorlink=AVR-Tutorial: Interrupts}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister SREG) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register temp1 übergeben, wobei temp1 vom Usercode definiert werden muss. &lt;br /&gt;
&lt;br /&gt;
der progger hat vergessen das Z reg in den sub&#039;s lcd_flash_string ...&lt;br /&gt;
zu sichern, aber danke für den Einstig&lt;br /&gt;
&lt;br /&gt;
Lars&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27991</id>
		<title>AVR-Tutorial: LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27991"/>
		<updated>2008-05-16T02:42:07Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: /* Anschluss an den Controller */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kaum ein elektronisches Gerät kommt heutzutage noch ohne ein LCD daher. Ist doch auch praktisch, Informationen im Klartext anzeigen zu können, ohne irgendwelche LEDs blinken zu lassen. Kein Wunder also, dass die häufigste Frage in Mikrocontroller-Foren ist: &amp;quot;Wie kann ich ein LCD anschließen?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-Bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-Bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-Bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-Bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-Bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-Bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4 Bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert. Ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitet hat. Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt, da wir wissen, welche Anschlüsse das LCD benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Pinnummer&amp;lt;BR&amp;gt;LCD || Bezeichnung || Anschluss&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |1 || Vss || GND&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |2 || Vcc || 5V&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |3 || Vee || GND oder [[Potentiometer | Poti]] &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |4 || RS || PD4 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |5 || RW || GND &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |6 || E || PD5 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |7 || DB0 || nicht angeschlossen &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |8 || DB1 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |9 || DB2 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |10 || DB3 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |11 || DB4 || PD0 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |12 || DB5 || PD1 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |13 || DB6 || PD2 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |14 || DB7 || PD3 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |15 || A || Vorsicht! Meistens nicht direkt an +5V anschließbar,&amp;lt;BR&amp;gt;nur über einen Vorwiderstand (z.B. 33&amp;amp;Omega;)!&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |16 || K || GND&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Ok alles ist verbunden. Wenn man jetzt den Strom einschaltet, sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display?&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung des LCDs im 4-Bit-Modus ==&lt;br /&gt;
&lt;br /&gt;
Um ein Byte zu übertragen muss man es erstmal in die beiden Nibbles zerlegen, die getrennt übertragen werden. Da das obere Nibble (Bit 4 - Bit 7) als erstes übertragen wird, die 4 Datenleitungen jedoch an die vier unteren Bits des Port D angeschlossen sind, muss man die beiden Nibbles des zu übertragenden Bytes erstmal vertauschen. Der AVR kennt dazu praktischerweise einen eigenen Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           swap r16               ; vertauscht die beiden Nibbles von r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus 0b00100101 wird so z.B. 0b01010010. &lt;br /&gt;
&lt;br /&gt;
Jetzt sind die Bits für die erste Phase der Übertragung an der richtigen Stelle. Trotzdem wollen wir das Ergebnis nicht einfach so mit &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; an den Port geben. Um die Hälfte des Bytes, die jetzt nicht an die Datenleitungen des LCDs gegeben wird auf null zu setzen, verwendet man folgenden Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           andi r16, 0b00001111   ; Nur die vier unteren (mit 1 markierten)&lt;br /&gt;
                                  ; Bits werden übernommen, alle anderen werden null&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also: Das obere Nibble wird erst mit dem unteren vertauscht damit es unten ist, dann wird das obere (das wir jetzt noch nicht brauchen) auf null gesetzt. &lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem LCD noch mitteilen, ob wir Daten oder Befehle senden wollen. Das machen wir, indem wir das Bit an dem RS angeschlossen ist (PD4) auf 0 (= Befehl senden) oder auf 1 setzen (= Daten senden). Um ein Bit in einem normalen Register zu setzen gibt es den Befehl sbr (Set Bit in Register). Dieser Befehl unterscheidet sich jedoch von sbi (das nur für IO-Register gilt) dadurch, dass man nicht die Nummer des zu setzenden Bits angibt, sondern eine Bitmaske. Das geht so: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbr r16, 0b00010000     ; Bit 4 setzen, alle anderen Bits bleiben gleich&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An PD4 ist RS angeschlossen, wenn wir r16 an den Port D ausgeben ist RS jetzt also high und das LCD erwartet Daten anstatt von Befehlen. &lt;br /&gt;
&lt;br /&gt;
Das Ergebnis können wir jetzt endlich direkt an den Port D übergeben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           out PORTD, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss vorher der Port D auf Ausgang geschalten werden, indem man 0xFF ins Datenrichtungsregister DDRD schreibt. &lt;br /&gt;
&lt;br /&gt;
Um dem LCD zu signalisieren, dass es das an den Datenleitungen anliegende Nibble übernehmen kann, wird die E-Leitung (Enable, an PD5 angeschlossen) auf high und kurz darauf wieder auf low gesetzt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbi PORTD, 5              ; Enable high&lt;br /&gt;
           nop                       ; 3 Taktzyklen warten (&amp;quot;nop&amp;quot; = nichts tun)&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5              ; Enable wieder low&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die eine Hälfte des Bytes wäre damit geschafft! Die andere Hälfte kommt direkt hinterher: alles was an der obenstehenden Vorgehensweise geändert werden muss ist, das &amp;quot;swap&amp;quot; (Vertauschen der beiden Nibbles) wegzulassen.&lt;br /&gt;
&lt;br /&gt;
== Initialisierung des Displays ==&lt;br /&gt;
&lt;br /&gt;
Allerdings gibt es noch ein Problem. Wenn ein LCD eingeschaltet wird, dann läuft es zunächst im 8 Bit Modus. Irgendwie muss das Display initialisiert und auf den 4 Bit Modus umgeschaltet werden, und zwar nur mit den 4 zur Verfügung stehenden Datenleitungen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn es Probleme gibt, dann meistens an diesem Punkt. Die &amp;quot;kompatiblen&amp;quot; Kontroller sind gelegentlich doch nicht 100% identisch. Es lohnt sich das Datenblatt (siehe Weblinks im Artikel [[LCD]]) genau zu lesen, in welcher Reihenfolge und mit welchen Abständen (Delays) die Initialisierungbefehle gesendet werden. Eine weitere Hilfe können Ansteuerungsbeispiele in Forenbeiträgen geben z.B.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79609#664268 (A) KS0066U oder Ähnliche --- LCD Treiber]&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 4 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Achtung: Im folgenden sind alle Bytes aus Sicht des LCD-Kontrollers angegeben! Da LCD-seitig nur die Leitungen DB4 - DB7 verwendet werden, ist daher immer nur das höherwertige Nibbel gültig. Durch die Art der Verschaltung (DB4 - DB7 wurde auf dem PORT an PD0 bis PD3 angeschlossen) ergibt sich dadurch eine Verschiebung, so dass das am Kontroller auszugebende Byte nibblemässig vertauscht ist!&lt;br /&gt;
&lt;br /&gt;
Die Sequenz, aus Sicht des Kontrollers, sieht so aus:&lt;br /&gt;
&lt;br /&gt;
* nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 4.1ms warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 100µs warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* $2 ins Steuerregister schreiben (RS = 0), dadurch wird auf 4 Bit Daten umstellt&lt;br /&gt;
* ab jetzt muss für die Übertragung eines Bytes jeweils das höherwertige Nibble und dann das niederwertige Nibble übertragen werden, wie oben beschrieben&lt;br /&gt;
* Mit dem Konfigurier-Befehl $20 das Display konfigurieren (4-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 8 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Der Vollständigkeit halber hier noch die notwendige Initialiserungssequenz für den 8 Bit Modus. Da hier die Daten komplett als 1 Byte übertragen werden können, sind einige Klimmzüge wie im 4 Bit Modus nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
* nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 4.1ms warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 100µs warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mit dem Konfigurier-Befehl 0x30 das Display konfigurieren (8-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
== Routinen zur LCD-Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
Die Routinen zur Kommunikation mit dem LCD sehen also so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000 (Anm.1)&lt;br /&gt;
           out PORTD, temp1             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000&lt;br /&gt;
           out PORTD, temp2             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out PORTD, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
 ;&lt;br /&gt;
 ; Bei höherem Takt (&amp;gt;= 8 MHz) kann es notwendig sein, &lt;br /&gt;
 ; vor dem Enable High 1-2 Wartetakte (nop) einzufügen. &lt;br /&gt;
 ; Siehe dazu http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi PORTD, 5                 ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5                 ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
&lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi  temp3,50&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall  delay5ms&lt;br /&gt;
           dec  temp3&lt;br /&gt;
           brne powerupwait&lt;br /&gt;
           ldi temp1, 0b00000011        ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out PORTD, temp1             ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00000010        ; 4bit-Modus einstellen&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00101000        ; 4Bit / 2 Zeilen / 5x8&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00001100        ; Display ein / Cursor aus / kein Blinken&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00000100        ; inkrement / kein Scrollen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi temp1, 0b00000001   ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi temp1, 0b00000010   ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anm.1: Siehe [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
Weitere Funktionen (wie z.B. Cursorposition verändern) sollten mit Hilfe der [[AVR-Tutorial:_LCD#Welche_Befehle_versteht_das_LCD.3F|Befehlscodeliste]] nicht schwer zu realisieren sein. Einfach den Code in temp laden, lcd_command aufrufen und ggf. eine Pause einfügen. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man die LCD-Ansteuerung auch an einen anderen Port des Mikrocontrollers &amp;quot;verschieben&amp;quot;: Wenn das LCD z.B. an Port B angeschlossen ist, dann reicht es im Programm alle &amp;quot;PORTD&amp;quot; durch &amp;quot;PORTB&amp;quot; und &amp;quot;DDRD&amp;quot; durch &amp;quot;DDRB&amp;quot; zu ersetzen. &lt;br /&gt;
&lt;br /&gt;
Wer eine höhere Taktfrequenz als 4 MHz verwendet, der sollte daran denken die Dauer der Verzögerungsschleifen anzupassen.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das diese Routinen zur Anzeige von Text verwendet, kann z.B. so aussehen (die Datei lcd-routines.asm muss sich im gleichen Verzeichnis befinden). Nach der Initialisierung wird zuerst der Displayinhalt gelöscht. Um dem LCD ein Zeichen zu schicken, lädt man es in temp1 und ruft die Routine &amp;quot;lcd_data&amp;quot; auf. Das folgende Beispiel zeigt das Wort &amp;quot;Test&amp;quot; auf dem LCD an. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/lcd-test.asm Download lcd-test.asm] &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;
&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, 0xFF    ; Port D = Ausgang&lt;br /&gt;
           out DDRD, temp1&lt;br /&gt;
&lt;br /&gt;
           rcall lcd_init     ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear    ; Display löschen&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;T&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;e&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           &lt;br /&gt;
           ldi temp1, &#039;s&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;t&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD-Routinen werden hier eingefügt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für längere Texte ist die Methode, jedes Zeichen einzeln in das Register zu laden und &amp;quot;lcd_data&amp;quot; aufzurufen natürlich nicht sehr praktisch. Dazu später aber mehr.&lt;br /&gt;
&lt;br /&gt;
Bisher wurden in Register immer irgendwelche Zahlenwerte geladen, aber in diesem Programm kommt plötzlich die Anweisung&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
vor. Wie ist diese zu verstehen? Passiert hier etwas grundlegend anderes als beim Laden einer Zahl in ein Register?&lt;br /&gt;
&lt;br /&gt;
Die Antwort darauf lautet: Nein. Auch hier wird letztendlich nur eine Zahl in ein Register geladen. Der Schlüssel zum Verständnis beruht darauf, dass zum LCD, so wie zu allen Ausgabegeräten, für die Ausgabe von Texten immer nur Zahlen übertragen werden, sog. Codes. Zum Beispiel könnte man vereinbaren, dass ein LCD, wenn es den Ausgabecode 65 erhält ein &#039;A&#039; anzeigt, bei einem Ausgabecode von 66 ein &#039;B&#039; usw. Naturgemäß gibt es daher viele verschiedene Code-Buchstaben Zuordnungen. Damit hier etwas Ordnung in das potentielle Chaos kommt, hat man sich bereits in der Steinzeit der Programmierung auf bestimmte Codetabellen geeinigt, von denen die verbreitetste sicherlich die ASCII-Zuordnung ist.&lt;br /&gt;
&lt;br /&gt;
==ASCII==&lt;br /&gt;
&lt;br /&gt;
ASCII steht für &#039;&#039;American Standard Code for Information Interchange&#039;&#039; und ist ein standardisierter Code zu Zeichenumsetzung. Die Codetabelle sieht hexadezimal dabei wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;.0&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x1&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x2&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x3&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x4&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x6&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x7&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x8&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x9&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xA&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xB&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xC&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xD&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xE&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xF&amp;lt;/th&amp;gt;&amp;lt;br&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;&#039;&#039;&#039;0x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NUL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SOH&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;STX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EOT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ENQ&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ACK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BEL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;HT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;VT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CR&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SO&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SI&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;&#039;&#039;&#039;1x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DLE&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NAK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SYN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CAN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EM&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SUB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ESC&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;US&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;&#039;&#039;&#039;2x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SP&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;!&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;#&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;%&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;amp;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;(&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;)&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;*&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;+&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;,&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;.&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;3x&#039;&#039;&#039;&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;&amp;lt;td&amp;gt;3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;9&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;=&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;gt;&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;4x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;B&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;C&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;D&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;F&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;G&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;H&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;I&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;J&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;L&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;M&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;N&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;O&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;&#039;&#039;&#039;5x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;P&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;R&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;S&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;T&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;U&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;V&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;W&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;X&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;[&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;\&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;]&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;^&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;6x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;`&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;a&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;b&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;c&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;d&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;e&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;f&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;g&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;h&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;i&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;j&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;k&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;l&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;m&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;n&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;o&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;&#039;&#039;&#039;7x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;p&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;r&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;s&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;t&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;u&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;v&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;w&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;{&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;|&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;~&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DEL&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 ersten beiden Zeilen enthalten die Codes für einige Steuerzeichen, ihre vollständige Beschreibung würde hier zu weit führen. Das Zeichen &#039;&#039;&#039;SP&#039;&#039;&#039; steht für ein &#039;&#039;Space&#039;&#039;, also ein Leerzeichen. &#039;&#039;&#039;BS&#039;&#039;&#039; steht für &#039;&#039;Backspace&#039;&#039;, also ein Zeichen zurück. &#039;&#039;&#039;DEL&#039;&#039;&#039; steht für &#039;&#039;Delete&#039;&#039;, also das Löschen eines Zeichens. &#039;&#039;&#039;CR&#039;&#039;&#039; steht für &#039;&#039;Carriage Return&#039;&#039;, also wörtlich: der Wagenrücklauf (einer Schreibmaschine), während &#039;&#039;&#039;LF&#039;&#039;&#039; für &#039;&#039;Line feed&#039;&#039;, also einen Zeilenvorschub steht.&lt;br /&gt;
&lt;br /&gt;
Der Assembler kennt diese Codetabelle und ersetzt die Zeile&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, $54&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
was letztendlich auch der Lesbarkeit des Programmes zugute kommt. Funktional besteht kein Unterschied zwischen den beiden Anweisungen. Beide bewirken, dass das Register temp1 mit dem Bitmuster 01010100 ( = hexadezimal 54, = dezimal 84 oder eben der ASCII Code für &#039;&#039;&#039;T&#039;&#039;&#039;) geladen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das LCD wiederrum kennt diese Code-Tabelle ebenfalls und wenn es über den Datenbus die Codezahl $54 zur Anzeige empfängt, dann schreibt es ein &#039;&#039;&#039;T&#039;&#039;&#039; an die aktuelle Cursorposition. Genauer gesagt, weiss das LCD nichts von einem &#039;&#039;&#039;T&#039;&#039;&#039;. Es sieht einfach in seinen internen Tabellen nach, welche Pixel beim Empfang der Codezahl $54 auf schwarz zu setzen sind. &#039;Zufällig&#039; sind das genau jene Pixel, die für uns Menschen ein &#039;&#039;&#039;T&#039;&#039;&#039; ergeben.&lt;br /&gt;
&lt;br /&gt;
==Welche Befehle versteht das LCD?==&lt;br /&gt;
&lt;br /&gt;
Auf dem LCD arbeitet ein Kontroller vom Typ HD44780. Diesen Kontroller versteht eine Reihe von Befehlen, die allesamt mittels lcd_command gesendet werden können. Ein Kommando ist dabei nichts anderes als ein Befehlsbyte, indem die verschiedenen Bits verschiedene Bedeutung besitzen:&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 0 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 1 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;der Zustand dieses Bits ist egal&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;sonstige Buchstaben&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;das Bit muss je nach gewünschter Funktionalität gesetzt werden. Die mögliche Funktionalität des jeweiligen Bits geht aus der Befehlsbeschreibung hervor&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Kommando &#039;ON/OFF Control&#039; soll benutzt werden um das Display einzuschalten, der Cursor soll eingeschaltet werden und der Cursor soll blinken.&lt;br /&gt;
Das Befehlsbyte ist so aufgebaut:&lt;br /&gt;
   0b00001dcb&lt;br /&gt;
Aus der Befehlsbeschreibung entnimmt man:&lt;br /&gt;
* Display ein bedeutet, dass an der Bitposition d eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor ein bedeutet, dass an der Bitposition c ein 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor blinken bedeutet, dass an der Bitposition b eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
Das dafür zu übertragende Befehlsbyte hat also die Gestalt 0b00001111 oder in hexadezimaler Schreibweise $0F&lt;br /&gt;
&lt;br /&gt;
===Clear display: 0b00000001===&lt;br /&gt;
&lt;br /&gt;
Die Anzeige wird gelöscht und der Ausgabecursor kehrt an die Home Position (links, erste Zeile) zurück&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Cursor home: 0b0000001x===&lt;br /&gt;
&lt;br /&gt;
Der Cursor kehrt an die Home Position (links, erste Zeile) zurück. Ein verschobenes Display wird auf die Grundeinstellung zurückgesetzt.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs bis 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Entry mode: 0b000001is===&lt;br /&gt;
&lt;br /&gt;
Legt die Cursor Richtung fest sowie eine mögliche Verschiebung des Displays fest&lt;br /&gt;
* i = 1, Cursorposition bei Ausgabe eines Zeichens erhöhen&lt;br /&gt;
* i = 0, Cursorposition bei Ausgabe eines Zeichens vermindern&lt;br /&gt;
* s = 1, Display wird gescrollt, wenn der Cursor das Ende/Anfang, je nach Einstellung von i, erreicht hat.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===On/off control: 0b00001dcb===&lt;br /&gt;
&lt;br /&gt;
Display insgesamt ein/ausschalten; den Cursor ein/ausschalten; den Cursor auf blinken schalten/blinken aus. Wenn das Display ausgeschaltet wird, geht der Inhalt des Displays nicht verloren. Der vorher angezeigte Text wird nach wiedereinschalten erneut angezeigt.&lt;br /&gt;
Ist der Cursor eingeschaltet, aber Blinken ausgeschaltet, so wird der Cursor als Cursorzeile in Pixelzeile 8 dargestellt. Ist Blinken eingeschaltet, wird der Cursor als blinkendes ausgefülltes Rechteck dargestellt, welches abwechselnd mit dem Buchstaben an dieser Stelle angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
* d = 0, Display aus&lt;br /&gt;
* d = 1, Display ein&lt;br /&gt;
* c = 0, Cursor aus&lt;br /&gt;
* c = 1, Cursor ein&lt;br /&gt;
* b = 0, Cursor blinken aus&lt;br /&gt;
* b = 1, Cursor blinken ein&lt;br /&gt;
 &lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Cursor/Scrollen: 0b0001srxx===&lt;br /&gt;
&lt;br /&gt;
Bewegt den Cursor oder scrollt das Display um eine Position entweder nach rechts oder nach links.&lt;br /&gt;
&lt;br /&gt;
* s = 1, Display scrollen&lt;br /&gt;
* s = 0, Cursor bewegen&lt;br /&gt;
* r = 1, nach rechts&lt;br /&gt;
* r = 0, nach links &lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Konfiguration: 0b001dnfxx===&lt;br /&gt;
&lt;br /&gt;
Einstellen der Interface Art, Modus, Font&lt;br /&gt;
* d = 0, 4-Bit Interface&lt;br /&gt;
* d = 1, 8-Bit Interface&lt;br /&gt;
* n = 0, 1 zeilig&lt;br /&gt;
* n = 1, 2 zeilig&lt;br /&gt;
* f = 0, 5x7 Pixel&lt;br /&gt;
* f = 1, 5x11 Pixel&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Character RAM Address Set: 0b01aaaaaa===&lt;br /&gt;
&lt;br /&gt;
Mit diesem Kommando werden maximal 8 selbst definierte Zeichen definiert. Dazu wird der Character RAM Zeiger auf den Anfang des Character Generator (CG) RAM gesetzt und das Zeichen durch die Ausgabe von 8 Byte definiert. Der Adress Zeiger wird nach Ausgabe jeder Pixelzeile (8Bit) vom LCD selbst erhöht. Nach Beendigung der Zeichendefinition muss die Schreibposition explizit mit dem Kommando &amp;quot;Display RAM Address Set&amp;quot; wieder in den DD-RAM Bereich gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
aaaaaa 6-bit CG RAM Adresse&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Display RAM Address Set: 0b1aaaaaaa===&lt;br /&gt;
&lt;br /&gt;
Den Cursor neu positionieren. Display Data (DD) Ram ist vom Character Generator (CG) Ram unabhängig. Der Adresszeiger wird bei Ausgabe eines Zeichens ins DD Ram automatisch erhöht. Das Display verhält sich so, als ob eine Zeile immer aus 32 logischen Zeichen besteht, von der, je nach konkretem Displaytyp (16 Zeichen, 20 Zeichen) immer nur ein Teil sichtbar ist.&lt;br /&gt;
&lt;br /&gt;
aaaaaaa 7-bit DD RAM Adresse. Auf 2-zeiligen Displays (und den meisten 16x1 Displays), kann die Adressangabe wie folgt interpretiert werden&lt;br /&gt;
&lt;br /&gt;
1laaaaaa&lt;br /&gt;
* l = Zeilennummer (0 oder 1)&lt;br /&gt;
* a = 6-Bit Spaltennummer&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
==Einschub: Code aufräumen==&lt;br /&gt;
&lt;br /&gt;
Es wird Zeit sich einmal etwas kritisch mit den bisher geschriebenen Funktionen auseinander zu setzen.&lt;br /&gt;
&lt;br /&gt;
===Portnamen aus dem Code herausziehen===&lt;br /&gt;
&lt;br /&gt;
Wenn wir die LCD-Funktionen einmal genauer betrachten, dann fällt sofort auf, daß über die Funktionen verstreut immer wieder das &#039;&#039;&#039;PORTD&#039;&#039;&#039; sowie einzelne Zahlen für die Pins an diesem Port auftauchen. Wenn das LCD an einem anderen Port betrieben werden soll, oder sich die Pin-Belegung ändert, dann muß an all diesen Stellen eine Anpassung vorgenommen werden. Dabei darf keine einzige Stelle übersehen werden, ansonsten würden die LCD-Funktionen nicht oder nicht vollständig funktionieren.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit dem vorzubeugen ist es, diese immer gleichbleibenden Dinge an den Anfang der LCD-Funktionen vorzuziehen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
.equ PIN_E    = 5&lt;br /&gt;
.equ PIN_RS   = 4&lt;br /&gt;
&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp1          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp2          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi LCD_PORT, PIN_E          ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi LCD_PORT, PIN_E          ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
 &lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf Ausgang&lt;br /&gt;
           out   LCD_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi   temp3,6&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           dec   temp3&lt;br /&gt;
           brne  powerupwait&lt;br /&gt;
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out   LCD_PORT, temp1        ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00101000      ; 4 Bot, 2 Zeilen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00001100      ; Display on, Cursor off&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00000100      ; endlich fertig&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi   temp1, 0b00000010      ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels &#039;&#039;&#039;.equ&#039;&#039;&#039; werden mit dem Assembler Textersetzungen vereinbart. Der Assembler ersetzt alle Vorkomnisse des Quelltextes durch den zu ersetzenden Text. Dadurch ist es zb. möglich alle Vorkommnisse von &#039;&#039;&#039;PORTD&#039;&#039;&#039; durch &#039;&#039;&#039;LCD_PORT&#039;&#039;&#039; auszutauschen. Wird das LCD an einen anderen Port, zb. &#039;&#039;&#039;PORTB&#039;&#039;&#039; gelegt, dann genügt es, einzig und alleine die Zeilen&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTB&lt;br /&gt;
.equ LCD_DDR  = DDRB&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu ersetzen. Der Assembler sorgt dann dafür, dass diese Portänderung an den relevanten Stellen im Code über die Textersetzungen einfliesst. Selbiges natürlich mit der Pin-Zuordnung.&lt;br /&gt;
&lt;br /&gt;
===Registerbenutzung===&lt;br /&gt;
&lt;br /&gt;
Bei diesen Funktionen mussten einige Register des Prozessors benutzt werden um darin Zwischenergebnisse zu speichern bzw. zu bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss dabei natürlich, dass es zu keinen Überschneidungen kommt. Solange nur jede Funktion jeweils für sich betrachtet wird, ist das kein Problem. In 20 oder 30 Code-Zeilen kann man gut verfolgen, welches Register wofür benutzt wird. Schwieriger wird es, wenn Funktionen wiederrum andere Funktionen aufrufen, die ihrerseits wieder Funktionen aufrufen usw. Jede dieser Funktionen benutzt einige Register und mit zunehmender Programmgröße wird es immer schwieriger zu verfolgen, welches Register zu welchem Zeitpunkt wofür benutzt wird.&lt;br /&gt;
&lt;br /&gt;
Speziell bei Basisfunktionen, wie diesen LCD-Funktionen, ist es daher oft ratsam, dafür zu sorgen, daß jede Funktion die Register wieder in dem Zustand hinterlässt, indem sie sie auch vorgefunden hat. Wir benötigen dazu wieder den Stack, auf dem die Registerinhalte bei Betreten einer Funktion zwischengespeichert werden und von dem die Register bei Verlassen einer Funktion wiederhergestellt werden.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir die Funktion&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion verändert das Register temp1. Um das Register abzusichern schreiben wir die Funktion um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           push  temp1                  ; temp1 auf dem Stack sichern&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           pop   temp1                  ; temp1 vom Stack wiederherstellen&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am besten hält man sich an die Regel: Jede Funktion ist dafür zuständig die Register zu sichern und wiederherzustellen, die sie auch selbst verändert. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; ruft die Funktionen &#039;&#039;&#039;lcd_command&#039;&#039;&#039; und &#039;&#039;&#039;delay5ms&#039;&#039;&#039; auf. Wenn diese Funktionen selbst wieder Register verändern (und das tun sie), so ist es die Aufgabe dieser Funktionen, sich um die Sicherung und das Wiederherstellen der entsprechenden Register zu kümmern. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; sollte sich nicht darum kümmern müssen. Auf diese Weise ist das Schlimmste, das einem passieren kann, das ein paar Register unnütz gesichert und wiederhergestellt werden. Das kostet zwar etwas Rechenzeit und etwas Speicherplatz auf dem Stack, ist aber immer noch besser als das andere Extrem: Nach einem Funktionsaufruf haben einige Register nicht mehr den Wert den sie haben sollten und das Programm rechnet mit falschen Zahlen weiter.&lt;br /&gt;
&lt;br /&gt;
===Lass den Assembler rechnen===&lt;br /&gt;
Betrachtet man den Code genauer, so fallen einige konstante Zahlenwerte auf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Das vorangestellte $ kennzeichnet die Zahl als Hexadezimalzahl) Der Code benötigt eine Warteschleife die mindestens 50µs dauert. Die beiden Befehle innerhalb der Schleife benötigen 3 Takte: 1 Takt für den &#039;&#039;&#039;dec&#039;&#039;&#039; und der &#039;&#039;&#039;brne&#039;&#039;&#039; benötigt 2 Takte wenn die Bedingung zutrifft, der Branch also genommen wird. Bei 4 Mhz werden also 4000000 / 3 * 50 / 1000000 = 66.6 Durchläufe durch die Schleife benötigt um eine Verzögerungszeit von 50µs (0.000050 Sekunden) zu erreichen, hexadezimal ausgedrückt: $42&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist: Bei anderen Taktfrequenzen müsste man nun jedesmal diese Berechnung machen und den entsprechenden Zahlenwert einsetzen. Das kann aber der Assembler genausogut erledigen. Am Anfang des Codes wird ein Eintrag definiert, der die Taktfrequenz festlegt. Traditionell heist dieser Eintrag &amp;lt;i&amp;gt;XTAL&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ XTAL  = 4000000&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 50 / 3 ) / 1000000&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An einer anderen Codestelle gibt es weitere derartige magische Zahlen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was geht hier vor?&lt;br /&gt;
Die innere Schleife benötigt wieder 3 Takte pro Durchlauf. Bei $C9 = 201 Durchläufen werden also 201 * 3 = 603 Takte verbraucht. In der äußeren Schleife kommen pro Durchlauf alo 1 + 603 + 1 + 2 = 607 Takte verbraucht. Da die äußere Schleife $21 = 33 mal wiederholt wird, werden 20031 Takte verbraucht. Bei 4Mhz benötigt der Prozessor 20031 / 4000000 = 0.005007 Sekunden, also 5 ms.&lt;br /&gt;
Wird der Wiederholwert für die innere Schleife bei $C9 belassen, so werden 4000000 / 607 * 5 / 1000 Wiederholungen der äusseren Schleife benötigt. Auch diese Berechnung kann wieder der Assembler übernehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 5 / 607 ) / 1000&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein kleines Problem kann bei der Verwendung dieses Verfahrens entstehen: Bei hohen Taktfrequenzen und großen Wartezeiten kann der berechnete Wert größer als 255 werden und man bekommt die Fehlermeldung &amp;quot;Operand(s) out of range&amp;quot; beim Assemblieren. Dieser Fall tritt zum Beispiel für obige Konstruktion bei einer Taktfrequenz von 16 MHz ein (genauer gesagt ab 15,3 MHz), während darunter XTAL beliebig geändert werden kann. Als einfachste Lösung bietet es sich an, die Zahl der Takte pro Schleifendurchlauf durch das Einfügen von &#039;&#039;&#039;nop&#039;&#039;&#039; zu erhöhen und die Berechnungsvorschrift anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Ausgabe eines konstanten Textes ==&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde schon einmal ein Text ausgegeben. Dies geschah durch Ausgabe von einzelnen Zeichen. Das können wir auch anders machen. Wir können den Text im Speicher ablegen und eine Funktion schreiben, die die einzelnen Zeichen aus dem Speicher holt und ausgibt. Dabei erhebt sich aber eine Fragestellung: Woher weiß die Funktion eigentlich, wie lange der Text ist? Die Antwort darauf lautet: Sie kann es nicht wissen. Wir müssen irgendwelche Vereinbarungen treffen, woran die Funktion erkennen kann, dass der Text zu Ende ist. Im Wesentlichen werden dazu 2 Methoden benutzt:&lt;br /&gt;
* Der Text enthält ein spezielles Zeichen, welches das Ende des Textes markiert&lt;br /&gt;
* Wir speichern nicht nur den Text selbst, sondern auch die Länge des Textes&lt;br /&gt;
Mit einer der beiden Methoden ist es der Textausgabefunktion dann ein Leichtes, den Text vollständig auszugeben.&lt;br /&gt;
&lt;br /&gt;
Wir werden uns im Weiteren dafür entscheiden, ein spezielles Zeichen, eine 0, dafür zu benutzen. Die Ausgabefunktionen werden dann etwas einfacher, als wenn bei der Ausgabe die Anzahl der bereits ausgegebenen Zeichen mitgezählt werden muss.&lt;br /&gt;
&lt;br /&gt;
Den Text selbst speichern wir im Flash-Speicher, also dort wo auch das Programm gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Einen konstanten Text aus dem Flash Speicher&lt;br /&gt;
 ; ausgeben. Der Text wird mit einer 0 beendet&lt;br /&gt;
lcd_flash_string:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_1:&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           rjmp  lcd_flash_string_1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_2:&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion benutzt den Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039; um das jeweils nächste Zeichen aus dem Flash Speicher in ein Register zur Weiterverarbeitung zu laden. Dazu wird der sog. &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; benutzt. So nennt man das Registerpaar &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;. Nach jedem Ladevorgang wird dabei durch den Befehl&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
dieser Z-Pointer um 1 erhöht. Mittels &#039;&#039;&#039;cpi&#039;&#039;&#039; wird das in das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; geladene Zeichen mit 0 verglichen. &#039;&#039;&#039;cpi&#039;&#039;&#039; vergleicht die beiden Zahlen und merkt sich das Ergebnis in einem speziellen Register in Form von Status Bits. &#039;&#039;&#039;cpi&#039;&#039;&#039; zieht dabei ganz einfach die beiden Zahlen voneinander ab. Sind sie gleich, so kommt da als Ergebnis 0 heraus und &#039;&#039;&#039;cpi&#039;&#039;&#039; setzt daher konsequenter Weise das Zero-Flag, das anzeigt, daß die vorhergegangene Operation ein 0 Ergebnis hatte.&#039;&#039;&#039;breq&#039;&#039;&#039; wertet diese Status-Bits aus. Wenn die vorhergegangene Operation ein 0-Ergebnis hatte, das Zero-Flag also gesetzt ist, dann wird ein Sprung zum angegebenen Label durchgeführt. In Summe bewirkt also die Sequenz&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
das das gelesene Zeichen mit 0 verglichen wird und falls das gelesene&lt;br /&gt;
Zeichen tatsächlich 0 war, an der Stelle lcd_flash_string_2 weiter gemacht wird. Im anderen Fall wird die bereits geschriebene Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aufgerufen, welche das Zeichen ausgibt. &#039;&#039;&#039;lcd_data&#039;&#039;&#039; erwartet dabei das Zeichen im Register &#039;&#039;&#039;temp1&#039;&#039;&#039;, genau in dem Register in welches wir vorher mittels &#039;&#039;&#039;lpm&#039;&#039;&#039; das Zeichen geladen hatten.&lt;br /&gt;
&lt;br /&gt;
Das verwendende Programm sieht dann 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 temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
           rcall lcd_init              ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear             ; Display löschen&lt;br /&gt;
 &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;
&lt;br /&gt;
           rcall lcd_flash_string      ; Unterprogramm gibt String aus der&lt;br /&gt;
                                       ; durch den Z-Pointer adressiert wird&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
           .db &amp;quot;Test&amp;quot;,0                ; Stringkonstante, durch eine 0&lt;br /&gt;
                                       ; abgeschlossen  &lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD Funktionen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Genaueres über die Verwendung unterschiedlicher Speicher findet sich im Kapitel [[AVR-Tutorial:_Speicher|Speicher]]&lt;br /&gt;
&lt;br /&gt;
==Zahlen ausgeben==&lt;br /&gt;
Um Zahlen, die beispielsweise in einem Register gespeichert sind, ausgeben zu können, ist es notwendig sich eine Textrepräsentierung der Zahl zu generieren. Die Zahl 123 wird also in den Text &amp;quot;123&amp;quot; umgewandelt welcher dann ausgegeben wird. Aus praktischen Gründen wird allerdings der Text nicht vollständig generiert (man müsste ihn ja irgendwo zwischenspeichern) sondern die einzelnen Buchstaben werden sofort ausgegeben, sobald sie bekannt sind.&lt;br /&gt;
&lt;br /&gt;
===Dezimal ausgeben===&lt;br /&gt;
Das Prinzip der Umwandlung ist einfach. Um herauszufinden wieviele Hunderter in der Zahl 123 enthalten sind, genügt es in einer Schleife immer wieder 100 von der Zahl abzuziehen und mitzuzählen wie oft dies gelang, bevor das Ergebnis negativ wurde. In diesem Fall lautet die Antwort: 1 mal, denn 123 - 100 macht 23. Versucht man erneut 100 anzuziehen, so ergibt sich eine negative Zahl.&lt;br /&gt;
Also muss eine &#039;1&#039; ausgeben werden. Die verbleibenden 23 werden weiterbehandelt, indem festgestellt wird wieviele Zehner darin enthalten sind. Auch hier wiederrum: In einer Schleife solange 10 abziehen, bis das Ergebnis nagativ wurde. Konkret geht das 2 mal gut, also muss das nächste auszugebende Zeichen ein &#039;2&#039; sein. Damit verbleiben noch die Einer, welche direkt in das entsprechende Zeichen umgewandelt werden können. In Summe hat man also an das Display die Zeichen &#039;1&#039; &#039;2&#039; &#039;3&#039; ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number:&lt;br /&gt;
           push  temp2            ; die Funktion verändert temp2, also sichern&lt;br /&gt;
                                  ; wir den Inhalt, um ihn am Ende wieder&lt;br /&gt;
                                  ; herstellen zu können&lt;br /&gt;
&lt;br /&gt;
           mov   temp2, temp1     ; das Register temp1 frei machen&lt;br /&gt;
                                  ; abzählen wieviele Hunderter&lt;br /&gt;
                                  ; in der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
           subi  temp2, 100       ; 100 abziehen&lt;br /&gt;
           brcs  lcd_number_2     ; ist dadurch ein Unterlauf entstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Hunderter mehr ...&lt;br /&gt;
           rjmp  lcd_number_1     ; ... und ab zur nächsten Runde&lt;br /&gt;
;&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorherhgehende Schleife 100 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; abzählen wieviele Zehner in&lt;br /&gt;
                  ; der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_3:&lt;br /&gt;
           subi  temp2, 10        ; 10 abziehen&lt;br /&gt;
           brcs  lcd_number_4     ; ist dadurch ein Unterlauf enstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Zehner mehr ...&lt;br /&gt;
           rjmp  lcd_number_3     ; ... und ab zur nächsten Runde&lt;br /&gt;
&lt;br /&gt;
                              ; die Zehnerstelle ausgeben&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; die übrig gebliebenen Einer&lt;br /&gt;
                  ; noch ausgeben&lt;br /&gt;
           ldi   temp1, &#039;0&#039;       ; die Zahl in temp2 ist jetzt im Bereich&lt;br /&gt;
           add   temp1, temp2     ; 0 bis 9. Einfach nur den ASCII Code für&lt;br /&gt;
           rcall lcd_data         ; &#039;0&#039; dazu addieren und wir erhalten dierekt&lt;br /&gt;
                                  ; den ASCII Code für die Ziffer&lt;br /&gt;
&lt;br /&gt;
           pop   temp2            ; den gesicherten Inhalt von temp2 wieder herstellen&lt;br /&gt;
           ret                    ; und zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beachte: Diese Funktion benutzt wiederrum die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039;. Anders als bei den bisherigen Aufrufen ist &#039;&#039;&#039;lcd_number&#039;&#039;&#039; aber darauf angewiesen, dass &#039;&#039;&#039;lcd_data&#039;&#039;&#039; das Register &#039;&#039;&#039;temp2&#039;&#039;&#039; unangetastet lässt. Falls sie es noch nicht getan haben, dann ist das jetzt die perfekte Gelegenheit, &#039;&#039;&#039;lcd_data&#039;&#039;&#039; mit den entsprechenden &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; Befehlen zu versehen. Sie sollten dies unbedingt zur Übung selbst machen. Am Ende muß die Funktion dann wie diese hier aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1           ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap  temp1                  ; Vertauschen&lt;br /&gt;
           andi  temp1, 0b00001111      ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr   temp1, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp1        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi  temp2, 0b00001111      ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr   temp2, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp2        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur ohne RS zu setzen&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           andi  temp1, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi  temp2, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz zur Funktionsweise der Funktion &#039;&#039;&#039;lcd_number&#039;&#039;&#039;: Die Zahl in einem Register bewegt sich im Wertebereich 0 bis 255. Um herauszufinden, wie die Hunderterstelle lautet, zieht die Funktion einfach in einer Schleife immer wieder 100 von der Schleife ab, bis bei der Subtraktion ein Unterlauf, angezeigt durch das Setzen des Carry-Bits bei der Subtraktion, entsteht. Die Anzahl wird im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; mitgezählt. Da dieses Register mit dem ASCII Code von &#039;0&#039; initialisiert wurde, und dieser ASCII Code bei jedem Schleifendurchlauf um 1 erhöht wird, können wir das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; direkt zur Ausgabe des Zeichens für die Hunderterstelle durch die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; benutzen. Völlig analog funktioniert auch die Ausgabe der Zehnerstelle.&lt;br /&gt;
&lt;br /&gt;
===Unterdrückung von führenden Nullen===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung: Diese Routine ist fehlerhaft&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt jede Zahl im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; immer mit 3 Stellen aus. Führende Nullen werden nicht unterdrückt. Möchte man dies ändern, so ist das ganz leicht möglich: Vor Ausgabe der Hunderterstelle bzw. Zehnerstelle muss lediglich überprüft werden, ob die Entsprechende Ausgabe eine &#039;0&#039; wäre. Ist sie das, so wird die Ausgabe übersprungen. Lediglich in der Einerstelle wird jede Ziffer wie errechnet ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ...&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben, wenn&lt;br /&gt;
                                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_2a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_2a:&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&lt;br /&gt;
           ...&lt;br /&gt;
                              ; die Zehnerstelle ausgeben, wenn&lt;br /&gt;
                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_4a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_4a:&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Verfahren, die einzelnen Stellen durch Subtraktion zu bestimmen, ist bei kleinen Zahlen eine durchaus gängige Alternative. Vor allem dann, wenn keine hardwaremäßige Unterstützung für Multiplikation und Division zur Verfügung steht. Ansonsten könnte man die die einzelnen Ziffern auch durch Division bestimmen. Das Prinzip ist folgendes (beispielhaft an der Zahl 52783 gezeigt)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   52783 / 10          -&amp;gt; 5278&lt;br /&gt;
   52783 - 5278 * 10   -&amp;gt;          3&lt;br /&gt;
&lt;br /&gt;
   5278 / 10           -&amp;gt; 527&lt;br /&gt;
   5278 - 527 * 10     -&amp;gt;          8&lt;br /&gt;
&lt;br /&gt;
   527 / 10            -&amp;gt; 52&lt;br /&gt;
   527 - 52 * 10       -&amp;gt;          7&lt;br /&gt;
&lt;br /&gt;
   52 / 10             -&amp;gt; 5&lt;br /&gt;
   52 - 5 * 10         -&amp;gt;          2&lt;br /&gt;
&lt;br /&gt;
   5 / 10              -&amp;gt; 0&lt;br /&gt;
   5 - 0 * 10          -&amp;gt;          5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Prinzip ist also die Restbildung bei einer fortgesetzten Division durch 10, wobei die einzelnen Ziffern in umgekehrter Reihenfolge ihrer Wertigkeit entstehen. Dadurch hat man aber ein Problem: Damit die Zeichen in der richtigen Reihenfolge ausgegeben werden können, muß man sie meistens zwischenspeichern um sie in der richtigen Reihenfole ausgeben zu können. Wird die Zahl in einem Feld von immer gleicher Größe ausgegeben, dann kann man auch die Zahl von rechts nach links ausgeben (bei einem LCD ist das möglich).&lt;br /&gt;
&lt;br /&gt;
===Hexadezimal ausgeben===&lt;br /&gt;
&lt;br /&gt;
Zu guter letzt hier noch eine Funktion, die eine Zahl aus dem Register &#039;&#039;&#039;temp1&#039;&#039;&#039; in hexadezimaler Form ausgibt. Die Funktion weist keine Besonderheiten auf und sollte unmittelbar verständlich sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen hexadezimal ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number_hex:&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           rcall lcd_number_hex_digit&lt;br /&gt;
           swap  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_number_hex_digit:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
           andi  temp1, $0F&lt;br /&gt;
           cpi   temp1, 10&lt;br /&gt;
           brlt  lcd_number_hex_digit_1&lt;br /&gt;
           subi  temp1, -( &#039;A&#039; - &#039;9&#039; - 1 ) ; es wird subi mit negativer Konstante verwendet, weil es kein addi gibt&lt;br /&gt;
lcd_number_hex_digit_1:&lt;br /&gt;
           subi  temp1, -&#039;0&#039;               ; ditto&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           &lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Eine 16-Bit Zahl aus einem Registerpärchen ausgeben===&lt;br /&gt;
&lt;br /&gt;
Um eine 16 Bit Zahl auszugeben wird wieder das bewährte Schema benutzt die einzelnen Stellen durch Subtraktion abzuzählen. Da es sich hierbei allerdings um eine 16 Bit Zahl handelt, müssen die Subtraktionen als 16-Bit Arithmetik ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 16 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp2 (low Byte) / temp3 (high Byte)&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number16:&lt;br /&gt;
           push  temp1&lt;br /&gt;
           push  temp2&lt;br /&gt;
           push  temp3&lt;br /&gt;
&lt;br /&gt;
; die Zehntausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number0:&lt;br /&gt;
           subi  temp2, low(10000)&lt;br /&gt;
           sbci  temp3, high(10000)&lt;br /&gt;
           brcs  lcd_number1&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number0&lt;br /&gt;
&lt;br /&gt;
; .. und ausgeben&lt;br /&gt;
lcd_number1:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-10000)&lt;br /&gt;
           sbci  temp3, high(-10000)&lt;br /&gt;
&lt;br /&gt;
; die Tausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number2:&lt;br /&gt;
           subi  temp2, low(1000)&lt;br /&gt;
           sbci  temp3, high(1000)&lt;br /&gt;
           brcs  lcd_number3&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number2&lt;br /&gt;
&lt;br /&gt;
; ... und ausgeben&lt;br /&gt;
lcd_number3:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-1000)&lt;br /&gt;
           sbci  temp3, high(-1000)&lt;br /&gt;
&lt;br /&gt;
; Als nächtes kommt die Hunderterstelle drann&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number4:&lt;br /&gt;
           subi  temp2, low(100)&lt;br /&gt;
           sbci  temp3, high(100)&lt;br /&gt;
           brcs  lcd_number5&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number4&lt;br /&gt;
&lt;br /&gt;
; und ausgeben&lt;br /&gt;
lcd_number5:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100&lt;br /&gt;
&lt;br /&gt;
; bleiben noch die Zehner&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number6:&lt;br /&gt;
           subi  temp2, 10&lt;br /&gt;
           brcs  lcd_number7&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number6&lt;br /&gt;
&lt;br /&gt;
; ausgeben ...&lt;br /&gt;
lcd_number7:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10&lt;br /&gt;
&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
           add   temp1, temp2&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
; fertig. Stack wieder aufräumen&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           pop   temp3&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Der überarbeitete, komplette Code==&lt;br /&gt;
&lt;br /&gt;
Hier also die komplett überarbeitete Version der LCD Funktionen.&lt;br /&gt;
&lt;br /&gt;
Die für die Benutzung relevanten Funktionen&lt;br /&gt;
* &#039;&#039;&#039;lcd_init&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_clear&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_home&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_data&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_flash_string&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number_hex&#039;&#039;&#039;&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; übergeben, wobei &#039;&#039;&#039;temp1&#039;&#039;&#039; vom Usercode definiert werden muss.&lt;br /&gt;
&lt;br /&gt;
[[Media:lcd-routines.asm|Download lcd-routines.asm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Stack|&lt;br /&gt;
zurücklink=AVR-Tutorial: Stack|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Interrupts|&lt;br /&gt;
vorlink=AVR-Tutorial: Interrupts}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister SREG) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register temp1 übergeben, wobei temp1 vom Usercode definiert werden muss. &lt;br /&gt;
&lt;br /&gt;
der progger hat vergessen das Z reg in den sub&#039;s lcd_flash_string ...&lt;br /&gt;
zu sichern, aber danke für den Einstig&lt;br /&gt;
&lt;br /&gt;
Lars&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27990</id>
		<title>AVR-Tutorial: LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27990"/>
		<updated>2008-05-16T02:41:30Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kaum ein elektronisches Gerät kommt heutzutage noch ohne ein LCD daher. Ist doch auch praktisch, Informationen im Klartext anzeigen zu können, ohne irgendwelche LEDs blinken zu lassen. Kein Wunder also, dass die häufigste Frage in Mikrocontroller-Foren ist: &amp;quot;Wie kann ich ein LCD anschließen?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-Bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-Bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-Bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-Bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-Bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-Bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4 Bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert. Ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitet hat. Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt, da wir wissen, welche Anschlüsse das LCD benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Pinnummer&amp;lt;BR&amp;gt;LCD || Bezeichnung || Anschluss&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |1 || Vss || GND&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |2 || Vcc || 5V&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |3 || Vee || GND oder [[Potentiometer | Poti]] &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |4 || RS || PD4 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |5 || RW || GND &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |6 || E || PD5 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |7 || DB0 || nicht angeschlossen &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |8 || DB1 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |9 || DB2 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |10 || DB3 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |11 || DB4 || PD0 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |12 || DB5 || PD1 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |13 || DB6 || PD2 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |14 || DB7 || PD3 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |15 || A || Vorsicht! Meistens nicht direkt an +5V anschliessbar,&amp;lt;BR&amp;gt;nur über einen Vorwiderstand (z.B. 33&amp;amp;Omega;)!&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |16 || K || GND&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Ok alles ist verbunden. Wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display?&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung des LCDs im 4-Bit-Modus ==&lt;br /&gt;
&lt;br /&gt;
Um ein Byte zu übertragen muss man es erstmal in die beiden Nibbles zerlegen, die getrennt übertragen werden. Da das obere Nibble (Bit 4 - Bit 7) als erstes übertragen wird, die 4 Datenleitungen jedoch an die vier unteren Bits des Port D angeschlossen sind, muss man die beiden Nibbles des zu übertragenden Bytes erstmal vertauschen. Der AVR kennt dazu praktischerweise einen eigenen Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           swap r16               ; vertauscht die beiden Nibbles von r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus 0b00100101 wird so z.B. 0b01010010. &lt;br /&gt;
&lt;br /&gt;
Jetzt sind die Bits für die erste Phase der Übertragung an der richtigen Stelle. Trotzdem wollen wir das Ergebnis nicht einfach so mit &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; an den Port geben. Um die Hälfte des Bytes, die jetzt nicht an die Datenleitungen des LCDs gegeben wird auf null zu setzen, verwendet man folgenden Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           andi r16, 0b00001111   ; Nur die vier unteren (mit 1 markierten)&lt;br /&gt;
                                  ; Bits werden übernommen, alle anderen werden null&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also: Das obere Nibble wird erst mit dem unteren vertauscht damit es unten ist, dann wird das obere (das wir jetzt noch nicht brauchen) auf null gesetzt. &lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem LCD noch mitteilen, ob wir Daten oder Befehle senden wollen. Das machen wir, indem wir das Bit an dem RS angeschlossen ist (PD4) auf 0 (= Befehl senden) oder auf 1 setzen (= Daten senden). Um ein Bit in einem normalen Register zu setzen gibt es den Befehl sbr (Set Bit in Register). Dieser Befehl unterscheidet sich jedoch von sbi (das nur für IO-Register gilt) dadurch, dass man nicht die Nummer des zu setzenden Bits angibt, sondern eine Bitmaske. Das geht so: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbr r16, 0b00010000     ; Bit 4 setzen, alle anderen Bits bleiben gleich&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An PD4 ist RS angeschlossen, wenn wir r16 an den Port D ausgeben ist RS jetzt also high und das LCD erwartet Daten anstatt von Befehlen. &lt;br /&gt;
&lt;br /&gt;
Das Ergebnis können wir jetzt endlich direkt an den Port D übergeben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           out PORTD, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss vorher der Port D auf Ausgang geschalten werden, indem man 0xFF ins Datenrichtungsregister DDRD schreibt. &lt;br /&gt;
&lt;br /&gt;
Um dem LCD zu signalisieren, dass es das an den Datenleitungen anliegende Nibble übernehmen kann, wird die E-Leitung (Enable, an PD5 angeschlossen) auf high und kurz darauf wieder auf low gesetzt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbi PORTD, 5              ; Enable high&lt;br /&gt;
           nop                       ; 3 Taktzyklen warten (&amp;quot;nop&amp;quot; = nichts tun)&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5              ; Enable wieder low&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die eine Hälfte des Bytes wäre damit geschafft! Die andere Hälfte kommt direkt hinterher: alles was an der obenstehenden Vorgehensweise geändert werden muss ist, das &amp;quot;swap&amp;quot; (Vertauschen der beiden Nibbles) wegzulassen.&lt;br /&gt;
&lt;br /&gt;
== Initialisierung des Displays ==&lt;br /&gt;
&lt;br /&gt;
Allerdings gibt es noch ein Problem. Wenn ein LCD eingeschaltet wird, dann läuft es zunächst im 8 Bit Modus. Irgendwie muss das Display initialisiert und auf den 4 Bit Modus umgeschaltet werden, und zwar nur mit den 4 zur Verfügung stehenden Datenleitungen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn es Probleme gibt, dann meistens an diesem Punkt. Die &amp;quot;kompatiblen&amp;quot; Kontroller sind gelegentlich doch nicht 100% identisch. Es lohnt sich das Datenblatt (siehe Weblinks im Artikel [[LCD]]) genau zu lesen, in welcher Reihenfolge und mit welchen Abständen (Delays) die Initialisierungbefehle gesendet werden. Eine weitere Hilfe können Ansteuerungsbeispiele in Forenbeiträgen geben z.B.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79609#664268 (A) KS0066U oder Ähnliche --- LCD Treiber]&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 4 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Achtung: Im folgenden sind alle Bytes aus Sicht des LCD-Kontrollers angegeben! Da LCD-seitig nur die Leitungen DB4 - DB7 verwendet werden, ist daher immer nur das höherwertige Nibbel gültig. Durch die Art der Verschaltung (DB4 - DB7 wurde auf dem PORT an PD0 bis PD3 angeschlossen) ergibt sich dadurch eine Verschiebung, so dass das am Kontroller auszugebende Byte nibblemässig vertauscht ist!&lt;br /&gt;
&lt;br /&gt;
Die Sequenz, aus Sicht des Kontrollers, sieht so aus:&lt;br /&gt;
&lt;br /&gt;
* nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 4.1ms warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 100µs warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* $2 ins Steuerregister schreiben (RS = 0), dadurch wird auf 4 Bit Daten umstellt&lt;br /&gt;
* ab jetzt muss für die Übertragung eines Bytes jeweils das höherwertige Nibble und dann das niederwertige Nibble übertragen werden, wie oben beschrieben&lt;br /&gt;
* Mit dem Konfigurier-Befehl $20 das Display konfigurieren (4-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 8 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Der Vollständigkeit halber hier noch die notwendige Initialiserungssequenz für den 8 Bit Modus. Da hier die Daten komplett als 1 Byte übertragen werden können, sind einige Klimmzüge wie im 4 Bit Modus nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
* nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 4.1ms warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 100µs warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mit dem Konfigurier-Befehl 0x30 das Display konfigurieren (8-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
== Routinen zur LCD-Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
Die Routinen zur Kommunikation mit dem LCD sehen also so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000 (Anm.1)&lt;br /&gt;
           out PORTD, temp1             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000&lt;br /&gt;
           out PORTD, temp2             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out PORTD, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
 ;&lt;br /&gt;
 ; Bei höherem Takt (&amp;gt;= 8 MHz) kann es notwendig sein, &lt;br /&gt;
 ; vor dem Enable High 1-2 Wartetakte (nop) einzufügen. &lt;br /&gt;
 ; Siehe dazu http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi PORTD, 5                 ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5                 ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
&lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi  temp3,50&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall  delay5ms&lt;br /&gt;
           dec  temp3&lt;br /&gt;
           brne powerupwait&lt;br /&gt;
           ldi temp1, 0b00000011        ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out PORTD, temp1             ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00000010        ; 4bit-Modus einstellen&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00101000        ; 4Bit / 2 Zeilen / 5x8&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00001100        ; Display ein / Cursor aus / kein Blinken&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00000100        ; inkrement / kein Scrollen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi temp1, 0b00000001   ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi temp1, 0b00000010   ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anm.1: Siehe [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
Weitere Funktionen (wie z.B. Cursorposition verändern) sollten mit Hilfe der [[AVR-Tutorial:_LCD#Welche_Befehle_versteht_das_LCD.3F|Befehlscodeliste]] nicht schwer zu realisieren sein. Einfach den Code in temp laden, lcd_command aufrufen und ggf. eine Pause einfügen. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man die LCD-Ansteuerung auch an einen anderen Port des Mikrocontrollers &amp;quot;verschieben&amp;quot;: Wenn das LCD z.B. an Port B angeschlossen ist, dann reicht es im Programm alle &amp;quot;PORTD&amp;quot; durch &amp;quot;PORTB&amp;quot; und &amp;quot;DDRD&amp;quot; durch &amp;quot;DDRB&amp;quot; zu ersetzen. &lt;br /&gt;
&lt;br /&gt;
Wer eine höhere Taktfrequenz als 4 MHz verwendet, der sollte daran denken die Dauer der Verzögerungsschleifen anzupassen.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das diese Routinen zur Anzeige von Text verwendet, kann z.B. so aussehen (die Datei lcd-routines.asm muss sich im gleichen Verzeichnis befinden). Nach der Initialisierung wird zuerst der Displayinhalt gelöscht. Um dem LCD ein Zeichen zu schicken, lädt man es in temp1 und ruft die Routine &amp;quot;lcd_data&amp;quot; auf. Das folgende Beispiel zeigt das Wort &amp;quot;Test&amp;quot; auf dem LCD an. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/lcd-test.asm Download lcd-test.asm] &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;
&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, 0xFF    ; Port D = Ausgang&lt;br /&gt;
           out DDRD, temp1&lt;br /&gt;
&lt;br /&gt;
           rcall lcd_init     ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear    ; Display löschen&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;T&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;e&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           &lt;br /&gt;
           ldi temp1, &#039;s&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;t&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD-Routinen werden hier eingefügt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für längere Texte ist die Methode, jedes Zeichen einzeln in das Register zu laden und &amp;quot;lcd_data&amp;quot; aufzurufen natürlich nicht sehr praktisch. Dazu später aber mehr.&lt;br /&gt;
&lt;br /&gt;
Bisher wurden in Register immer irgendwelche Zahlenwerte geladen, aber in diesem Programm kommt plötzlich die Anweisung&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
vor. Wie ist diese zu verstehen? Passiert hier etwas grundlegend anderes als beim Laden einer Zahl in ein Register?&lt;br /&gt;
&lt;br /&gt;
Die Antwort darauf lautet: Nein. Auch hier wird letztendlich nur eine Zahl in ein Register geladen. Der Schlüssel zum Verständnis beruht darauf, dass zum LCD, so wie zu allen Ausgabegeräten, für die Ausgabe von Texten immer nur Zahlen übertragen werden, sog. Codes. Zum Beispiel könnte man vereinbaren, dass ein LCD, wenn es den Ausgabecode 65 erhält ein &#039;A&#039; anzeigt, bei einem Ausgabecode von 66 ein &#039;B&#039; usw. Naturgemäß gibt es daher viele verschiedene Code-Buchstaben Zuordnungen. Damit hier etwas Ordnung in das potentielle Chaos kommt, hat man sich bereits in der Steinzeit der Programmierung auf bestimmte Codetabellen geeinigt, von denen die verbreitetste sicherlich die ASCII-Zuordnung ist.&lt;br /&gt;
&lt;br /&gt;
==ASCII==&lt;br /&gt;
&lt;br /&gt;
ASCII steht für &#039;&#039;American Standard Code for Information Interchange&#039;&#039; und ist ein standardisierter Code zu Zeichenumsetzung. Die Codetabelle sieht hexadezimal dabei wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;.0&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x1&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x2&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x3&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x4&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x6&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x7&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x8&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x9&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xA&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xB&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xC&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xD&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xE&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xF&amp;lt;/th&amp;gt;&amp;lt;br&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;&#039;&#039;&#039;0x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NUL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SOH&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;STX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EOT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ENQ&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ACK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BEL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;HT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;VT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CR&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SO&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SI&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;&#039;&#039;&#039;1x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DLE&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NAK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SYN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CAN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EM&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SUB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ESC&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;US&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;&#039;&#039;&#039;2x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SP&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;!&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;#&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;%&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;amp;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;(&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;)&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;*&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;+&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;,&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;.&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;3x&#039;&#039;&#039;&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;&amp;lt;td&amp;gt;3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;9&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;=&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;gt;&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;4x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;B&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;C&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;D&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;F&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;G&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;H&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;I&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;J&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;L&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;M&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;N&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;O&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;&#039;&#039;&#039;5x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;P&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;R&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;S&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;T&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;U&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;V&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;W&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;X&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;[&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;\&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;]&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;^&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;6x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;`&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;a&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;b&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;c&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;d&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;e&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;f&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;g&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;h&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;i&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;j&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;k&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;l&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;m&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;n&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;o&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;&#039;&#039;&#039;7x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;p&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;r&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;s&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;t&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;u&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;v&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;w&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;{&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;|&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;~&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DEL&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 ersten beiden Zeilen enthalten die Codes für einige Steuerzeichen, ihre vollständige Beschreibung würde hier zu weit führen. Das Zeichen &#039;&#039;&#039;SP&#039;&#039;&#039; steht für ein &#039;&#039;Space&#039;&#039;, also ein Leerzeichen. &#039;&#039;&#039;BS&#039;&#039;&#039; steht für &#039;&#039;Backspace&#039;&#039;, also ein Zeichen zurück. &#039;&#039;&#039;DEL&#039;&#039;&#039; steht für &#039;&#039;Delete&#039;&#039;, also das Löschen eines Zeichens. &#039;&#039;&#039;CR&#039;&#039;&#039; steht für &#039;&#039;Carriage Return&#039;&#039;, also wörtlich: der Wagenrücklauf (einer Schreibmaschine), während &#039;&#039;&#039;LF&#039;&#039;&#039; für &#039;&#039;Line feed&#039;&#039;, also einen Zeilenvorschub steht.&lt;br /&gt;
&lt;br /&gt;
Der Assembler kennt diese Codetabelle und ersetzt die Zeile&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, $54&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
was letztendlich auch der Lesbarkeit des Programmes zugute kommt. Funktional besteht kein Unterschied zwischen den beiden Anweisungen. Beide bewirken, dass das Register temp1 mit dem Bitmuster 01010100 ( = hexadezimal 54, = dezimal 84 oder eben der ASCII Code für &#039;&#039;&#039;T&#039;&#039;&#039;) geladen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das LCD wiederrum kennt diese Code-Tabelle ebenfalls und wenn es über den Datenbus die Codezahl $54 zur Anzeige empfängt, dann schreibt es ein &#039;&#039;&#039;T&#039;&#039;&#039; an die aktuelle Cursorposition. Genauer gesagt, weiss das LCD nichts von einem &#039;&#039;&#039;T&#039;&#039;&#039;. Es sieht einfach in seinen internen Tabellen nach, welche Pixel beim Empfang der Codezahl $54 auf schwarz zu setzen sind. &#039;Zufällig&#039; sind das genau jene Pixel, die für uns Menschen ein &#039;&#039;&#039;T&#039;&#039;&#039; ergeben.&lt;br /&gt;
&lt;br /&gt;
==Welche Befehle versteht das LCD?==&lt;br /&gt;
&lt;br /&gt;
Auf dem LCD arbeitet ein Kontroller vom Typ HD44780. Diesen Kontroller versteht eine Reihe von Befehlen, die allesamt mittels lcd_command gesendet werden können. Ein Kommando ist dabei nichts anderes als ein Befehlsbyte, indem die verschiedenen Bits verschiedene Bedeutung besitzen:&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 0 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 1 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;der Zustand dieses Bits ist egal&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;sonstige Buchstaben&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;das Bit muss je nach gewünschter Funktionalität gesetzt werden. Die mögliche Funktionalität des jeweiligen Bits geht aus der Befehlsbeschreibung hervor&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Kommando &#039;ON/OFF Control&#039; soll benutzt werden um das Display einzuschalten, der Cursor soll eingeschaltet werden und der Cursor soll blinken.&lt;br /&gt;
Das Befehlsbyte ist so aufgebaut:&lt;br /&gt;
   0b00001dcb&lt;br /&gt;
Aus der Befehlsbeschreibung entnimmt man:&lt;br /&gt;
* Display ein bedeutet, dass an der Bitposition d eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor ein bedeutet, dass an der Bitposition c ein 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor blinken bedeutet, dass an der Bitposition b eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
Das dafür zu übertragende Befehlsbyte hat also die Gestalt 0b00001111 oder in hexadezimaler Schreibweise $0F&lt;br /&gt;
&lt;br /&gt;
===Clear display: 0b00000001===&lt;br /&gt;
&lt;br /&gt;
Die Anzeige wird gelöscht und der Ausgabecursor kehrt an die Home Position (links, erste Zeile) zurück&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Cursor home: 0b0000001x===&lt;br /&gt;
&lt;br /&gt;
Der Cursor kehrt an die Home Position (links, erste Zeile) zurück. Ein verschobenes Display wird auf die Grundeinstellung zurückgesetzt.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs bis 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Entry mode: 0b000001is===&lt;br /&gt;
&lt;br /&gt;
Legt die Cursor Richtung fest sowie eine mögliche Verschiebung des Displays fest&lt;br /&gt;
* i = 1, Cursorposition bei Ausgabe eines Zeichens erhöhen&lt;br /&gt;
* i = 0, Cursorposition bei Ausgabe eines Zeichens vermindern&lt;br /&gt;
* s = 1, Display wird gescrollt, wenn der Cursor das Ende/Anfang, je nach Einstellung von i, erreicht hat.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===On/off control: 0b00001dcb===&lt;br /&gt;
&lt;br /&gt;
Display insgesamt ein/ausschalten; den Cursor ein/ausschalten; den Cursor auf blinken schalten/blinken aus. Wenn das Display ausgeschaltet wird, geht der Inhalt des Displays nicht verloren. Der vorher angezeigte Text wird nach wiedereinschalten erneut angezeigt.&lt;br /&gt;
Ist der Cursor eingeschaltet, aber Blinken ausgeschaltet, so wird der Cursor als Cursorzeile in Pixelzeile 8 dargestellt. Ist Blinken eingeschaltet, wird der Cursor als blinkendes ausgefülltes Rechteck dargestellt, welches abwechselnd mit dem Buchstaben an dieser Stelle angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
* d = 0, Display aus&lt;br /&gt;
* d = 1, Display ein&lt;br /&gt;
* c = 0, Cursor aus&lt;br /&gt;
* c = 1, Cursor ein&lt;br /&gt;
* b = 0, Cursor blinken aus&lt;br /&gt;
* b = 1, Cursor blinken ein&lt;br /&gt;
 &lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Cursor/Scrollen: 0b0001srxx===&lt;br /&gt;
&lt;br /&gt;
Bewegt den Cursor oder scrollt das Display um eine Position entweder nach rechts oder nach links.&lt;br /&gt;
&lt;br /&gt;
* s = 1, Display scrollen&lt;br /&gt;
* s = 0, Cursor bewegen&lt;br /&gt;
* r = 1, nach rechts&lt;br /&gt;
* r = 0, nach links &lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Konfiguration: 0b001dnfxx===&lt;br /&gt;
&lt;br /&gt;
Einstellen der Interface Art, Modus, Font&lt;br /&gt;
* d = 0, 4-Bit Interface&lt;br /&gt;
* d = 1, 8-Bit Interface&lt;br /&gt;
* n = 0, 1 zeilig&lt;br /&gt;
* n = 1, 2 zeilig&lt;br /&gt;
* f = 0, 5x7 Pixel&lt;br /&gt;
* f = 1, 5x11 Pixel&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Character RAM Address Set: 0b01aaaaaa===&lt;br /&gt;
&lt;br /&gt;
Mit diesem Kommando werden maximal 8 selbst definierte Zeichen definiert. Dazu wird der Character RAM Zeiger auf den Anfang des Character Generator (CG) RAM gesetzt und das Zeichen durch die Ausgabe von 8 Byte definiert. Der Adress Zeiger wird nach Ausgabe jeder Pixelzeile (8Bit) vom LCD selbst erhöht. Nach Beendigung der Zeichendefinition muss die Schreibposition explizit mit dem Kommando &amp;quot;Display RAM Address Set&amp;quot; wieder in den DD-RAM Bereich gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
aaaaaa 6-bit CG RAM Adresse&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Display RAM Address Set: 0b1aaaaaaa===&lt;br /&gt;
&lt;br /&gt;
Den Cursor neu positionieren. Display Data (DD) Ram ist vom Character Generator (CG) Ram unabhängig. Der Adresszeiger wird bei Ausgabe eines Zeichens ins DD Ram automatisch erhöht. Das Display verhält sich so, als ob eine Zeile immer aus 32 logischen Zeichen besteht, von der, je nach konkretem Displaytyp (16 Zeichen, 20 Zeichen) immer nur ein Teil sichtbar ist.&lt;br /&gt;
&lt;br /&gt;
aaaaaaa 7-bit DD RAM Adresse. Auf 2-zeiligen Displays (und den meisten 16x1 Displays), kann die Adressangabe wie folgt interpretiert werden&lt;br /&gt;
&lt;br /&gt;
1laaaaaa&lt;br /&gt;
* l = Zeilennummer (0 oder 1)&lt;br /&gt;
* a = 6-Bit Spaltennummer&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
==Einschub: Code aufräumen==&lt;br /&gt;
&lt;br /&gt;
Es wird Zeit sich einmal etwas kritisch mit den bisher geschriebenen Funktionen auseinander zu setzen.&lt;br /&gt;
&lt;br /&gt;
===Portnamen aus dem Code herausziehen===&lt;br /&gt;
&lt;br /&gt;
Wenn wir die LCD-Funktionen einmal genauer betrachten, dann fällt sofort auf, daß über die Funktionen verstreut immer wieder das &#039;&#039;&#039;PORTD&#039;&#039;&#039; sowie einzelne Zahlen für die Pins an diesem Port auftauchen. Wenn das LCD an einem anderen Port betrieben werden soll, oder sich die Pin-Belegung ändert, dann muß an all diesen Stellen eine Anpassung vorgenommen werden. Dabei darf keine einzige Stelle übersehen werden, ansonsten würden die LCD-Funktionen nicht oder nicht vollständig funktionieren.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit dem vorzubeugen ist es, diese immer gleichbleibenden Dinge an den Anfang der LCD-Funktionen vorzuziehen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
.equ PIN_E    = 5&lt;br /&gt;
.equ PIN_RS   = 4&lt;br /&gt;
&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp1          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp2          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi LCD_PORT, PIN_E          ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi LCD_PORT, PIN_E          ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
 &lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf Ausgang&lt;br /&gt;
           out   LCD_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi   temp3,6&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           dec   temp3&lt;br /&gt;
           brne  powerupwait&lt;br /&gt;
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out   LCD_PORT, temp1        ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00101000      ; 4 Bot, 2 Zeilen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00001100      ; Display on, Cursor off&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00000100      ; endlich fertig&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi   temp1, 0b00000010      ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels &#039;&#039;&#039;.equ&#039;&#039;&#039; werden mit dem Assembler Textersetzungen vereinbart. Der Assembler ersetzt alle Vorkomnisse des Quelltextes durch den zu ersetzenden Text. Dadurch ist es zb. möglich alle Vorkommnisse von &#039;&#039;&#039;PORTD&#039;&#039;&#039; durch &#039;&#039;&#039;LCD_PORT&#039;&#039;&#039; auszutauschen. Wird das LCD an einen anderen Port, zb. &#039;&#039;&#039;PORTB&#039;&#039;&#039; gelegt, dann genügt es, einzig und alleine die Zeilen&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTB&lt;br /&gt;
.equ LCD_DDR  = DDRB&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu ersetzen. Der Assembler sorgt dann dafür, dass diese Portänderung an den relevanten Stellen im Code über die Textersetzungen einfliesst. Selbiges natürlich mit der Pin-Zuordnung.&lt;br /&gt;
&lt;br /&gt;
===Registerbenutzung===&lt;br /&gt;
&lt;br /&gt;
Bei diesen Funktionen mussten einige Register des Prozessors benutzt werden um darin Zwischenergebnisse zu speichern bzw. zu bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss dabei natürlich, dass es zu keinen Überschneidungen kommt. Solange nur jede Funktion jeweils für sich betrachtet wird, ist das kein Problem. In 20 oder 30 Code-Zeilen kann man gut verfolgen, welches Register wofür benutzt wird. Schwieriger wird es, wenn Funktionen wiederrum andere Funktionen aufrufen, die ihrerseits wieder Funktionen aufrufen usw. Jede dieser Funktionen benutzt einige Register und mit zunehmender Programmgröße wird es immer schwieriger zu verfolgen, welches Register zu welchem Zeitpunkt wofür benutzt wird.&lt;br /&gt;
&lt;br /&gt;
Speziell bei Basisfunktionen, wie diesen LCD-Funktionen, ist es daher oft ratsam, dafür zu sorgen, daß jede Funktion die Register wieder in dem Zustand hinterlässt, indem sie sie auch vorgefunden hat. Wir benötigen dazu wieder den Stack, auf dem die Registerinhalte bei Betreten einer Funktion zwischengespeichert werden und von dem die Register bei Verlassen einer Funktion wiederhergestellt werden.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir die Funktion&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion verändert das Register temp1. Um das Register abzusichern schreiben wir die Funktion um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           push  temp1                  ; temp1 auf dem Stack sichern&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           pop   temp1                  ; temp1 vom Stack wiederherstellen&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am besten hält man sich an die Regel: Jede Funktion ist dafür zuständig die Register zu sichern und wiederherzustellen, die sie auch selbst verändert. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; ruft die Funktionen &#039;&#039;&#039;lcd_command&#039;&#039;&#039; und &#039;&#039;&#039;delay5ms&#039;&#039;&#039; auf. Wenn diese Funktionen selbst wieder Register verändern (und das tun sie), so ist es die Aufgabe dieser Funktionen, sich um die Sicherung und das Wiederherstellen der entsprechenden Register zu kümmern. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; sollte sich nicht darum kümmern müssen. Auf diese Weise ist das Schlimmste, das einem passieren kann, das ein paar Register unnütz gesichert und wiederhergestellt werden. Das kostet zwar etwas Rechenzeit und etwas Speicherplatz auf dem Stack, ist aber immer noch besser als das andere Extrem: Nach einem Funktionsaufruf haben einige Register nicht mehr den Wert den sie haben sollten und das Programm rechnet mit falschen Zahlen weiter.&lt;br /&gt;
&lt;br /&gt;
===Lass den Assembler rechnen===&lt;br /&gt;
Betrachtet man den Code genauer, so fallen einige konstante Zahlenwerte auf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Das vorangestellte $ kennzeichnet die Zahl als Hexadezimalzahl) Der Code benötigt eine Warteschleife die mindestens 50µs dauert. Die beiden Befehle innerhalb der Schleife benötigen 3 Takte: 1 Takt für den &#039;&#039;&#039;dec&#039;&#039;&#039; und der &#039;&#039;&#039;brne&#039;&#039;&#039; benötigt 2 Takte wenn die Bedingung zutrifft, der Branch also genommen wird. Bei 4 Mhz werden also 4000000 / 3 * 50 / 1000000 = 66.6 Durchläufe durch die Schleife benötigt um eine Verzögerungszeit von 50µs (0.000050 Sekunden) zu erreichen, hexadezimal ausgedrückt: $42&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist: Bei anderen Taktfrequenzen müsste man nun jedesmal diese Berechnung machen und den entsprechenden Zahlenwert einsetzen. Das kann aber der Assembler genausogut erledigen. Am Anfang des Codes wird ein Eintrag definiert, der die Taktfrequenz festlegt. Traditionell heist dieser Eintrag &amp;lt;i&amp;gt;XTAL&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ XTAL  = 4000000&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 50 / 3 ) / 1000000&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An einer anderen Codestelle gibt es weitere derartige magische Zahlen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was geht hier vor?&lt;br /&gt;
Die innere Schleife benötigt wieder 3 Takte pro Durchlauf. Bei $C9 = 201 Durchläufen werden also 201 * 3 = 603 Takte verbraucht. In der äußeren Schleife kommen pro Durchlauf alo 1 + 603 + 1 + 2 = 607 Takte verbraucht. Da die äußere Schleife $21 = 33 mal wiederholt wird, werden 20031 Takte verbraucht. Bei 4Mhz benötigt der Prozessor 20031 / 4000000 = 0.005007 Sekunden, also 5 ms.&lt;br /&gt;
Wird der Wiederholwert für die innere Schleife bei $C9 belassen, so werden 4000000 / 607 * 5 / 1000 Wiederholungen der äusseren Schleife benötigt. Auch diese Berechnung kann wieder der Assembler übernehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 5 / 607 ) / 1000&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein kleines Problem kann bei der Verwendung dieses Verfahrens entstehen: Bei hohen Taktfrequenzen und großen Wartezeiten kann der berechnete Wert größer als 255 werden und man bekommt die Fehlermeldung &amp;quot;Operand(s) out of range&amp;quot; beim Assemblieren. Dieser Fall tritt zum Beispiel für obige Konstruktion bei einer Taktfrequenz von 16 MHz ein (genauer gesagt ab 15,3 MHz), während darunter XTAL beliebig geändert werden kann. Als einfachste Lösung bietet es sich an, die Zahl der Takte pro Schleifendurchlauf durch das Einfügen von &#039;&#039;&#039;nop&#039;&#039;&#039; zu erhöhen und die Berechnungsvorschrift anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Ausgabe eines konstanten Textes ==&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde schon einmal ein Text ausgegeben. Dies geschah durch Ausgabe von einzelnen Zeichen. Das können wir auch anders machen. Wir können den Text im Speicher ablegen und eine Funktion schreiben, die die einzelnen Zeichen aus dem Speicher holt und ausgibt. Dabei erhebt sich aber eine Fragestellung: Woher weiß die Funktion eigentlich, wie lange der Text ist? Die Antwort darauf lautet: Sie kann es nicht wissen. Wir müssen irgendwelche Vereinbarungen treffen, woran die Funktion erkennen kann, dass der Text zu Ende ist. Im Wesentlichen werden dazu 2 Methoden benutzt:&lt;br /&gt;
* Der Text enthält ein spezielles Zeichen, welches das Ende des Textes markiert&lt;br /&gt;
* Wir speichern nicht nur den Text selbst, sondern auch die Länge des Textes&lt;br /&gt;
Mit einer der beiden Methoden ist es der Textausgabefunktion dann ein Leichtes, den Text vollständig auszugeben.&lt;br /&gt;
&lt;br /&gt;
Wir werden uns im Weiteren dafür entscheiden, ein spezielles Zeichen, eine 0, dafür zu benutzen. Die Ausgabefunktionen werden dann etwas einfacher, als wenn bei der Ausgabe die Anzahl der bereits ausgegebenen Zeichen mitgezählt werden muss.&lt;br /&gt;
&lt;br /&gt;
Den Text selbst speichern wir im Flash-Speicher, also dort wo auch das Programm gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Einen konstanten Text aus dem Flash Speicher&lt;br /&gt;
 ; ausgeben. Der Text wird mit einer 0 beendet&lt;br /&gt;
lcd_flash_string:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_1:&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           rjmp  lcd_flash_string_1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_2:&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion benutzt den Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039; um das jeweils nächste Zeichen aus dem Flash Speicher in ein Register zur Weiterverarbeitung zu laden. Dazu wird der sog. &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; benutzt. So nennt man das Registerpaar &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;. Nach jedem Ladevorgang wird dabei durch den Befehl&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
dieser Z-Pointer um 1 erhöht. Mittels &#039;&#039;&#039;cpi&#039;&#039;&#039; wird das in das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; geladene Zeichen mit 0 verglichen. &#039;&#039;&#039;cpi&#039;&#039;&#039; vergleicht die beiden Zahlen und merkt sich das Ergebnis in einem speziellen Register in Form von Status Bits. &#039;&#039;&#039;cpi&#039;&#039;&#039; zieht dabei ganz einfach die beiden Zahlen voneinander ab. Sind sie gleich, so kommt da als Ergebnis 0 heraus und &#039;&#039;&#039;cpi&#039;&#039;&#039; setzt daher konsequenter Weise das Zero-Flag, das anzeigt, daß die vorhergegangene Operation ein 0 Ergebnis hatte.&#039;&#039;&#039;breq&#039;&#039;&#039; wertet diese Status-Bits aus. Wenn die vorhergegangene Operation ein 0-Ergebnis hatte, das Zero-Flag also gesetzt ist, dann wird ein Sprung zum angegebenen Label durchgeführt. In Summe bewirkt also die Sequenz&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
das das gelesene Zeichen mit 0 verglichen wird und falls das gelesene&lt;br /&gt;
Zeichen tatsächlich 0 war, an der Stelle lcd_flash_string_2 weiter gemacht wird. Im anderen Fall wird die bereits geschriebene Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aufgerufen, welche das Zeichen ausgibt. &#039;&#039;&#039;lcd_data&#039;&#039;&#039; erwartet dabei das Zeichen im Register &#039;&#039;&#039;temp1&#039;&#039;&#039;, genau in dem Register in welches wir vorher mittels &#039;&#039;&#039;lpm&#039;&#039;&#039; das Zeichen geladen hatten.&lt;br /&gt;
&lt;br /&gt;
Das verwendende Programm sieht dann 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 temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
           rcall lcd_init              ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear             ; Display löschen&lt;br /&gt;
 &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;
&lt;br /&gt;
           rcall lcd_flash_string      ; Unterprogramm gibt String aus der&lt;br /&gt;
                                       ; durch den Z-Pointer adressiert wird&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
           .db &amp;quot;Test&amp;quot;,0                ; Stringkonstante, durch eine 0&lt;br /&gt;
                                       ; abgeschlossen  &lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD Funktionen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Genaueres über die Verwendung unterschiedlicher Speicher findet sich im Kapitel [[AVR-Tutorial:_Speicher|Speicher]]&lt;br /&gt;
&lt;br /&gt;
==Zahlen ausgeben==&lt;br /&gt;
Um Zahlen, die beispielsweise in einem Register gespeichert sind, ausgeben zu können, ist es notwendig sich eine Textrepräsentierung der Zahl zu generieren. Die Zahl 123 wird also in den Text &amp;quot;123&amp;quot; umgewandelt welcher dann ausgegeben wird. Aus praktischen Gründen wird allerdings der Text nicht vollständig generiert (man müsste ihn ja irgendwo zwischenspeichern) sondern die einzelnen Buchstaben werden sofort ausgegeben, sobald sie bekannt sind.&lt;br /&gt;
&lt;br /&gt;
===Dezimal ausgeben===&lt;br /&gt;
Das Prinzip der Umwandlung ist einfach. Um herauszufinden wieviele Hunderter in der Zahl 123 enthalten sind, genügt es in einer Schleife immer wieder 100 von der Zahl abzuziehen und mitzuzählen wie oft dies gelang, bevor das Ergebnis negativ wurde. In diesem Fall lautet die Antwort: 1 mal, denn 123 - 100 macht 23. Versucht man erneut 100 anzuziehen, so ergibt sich eine negative Zahl.&lt;br /&gt;
Also muss eine &#039;1&#039; ausgeben werden. Die verbleibenden 23 werden weiterbehandelt, indem festgestellt wird wieviele Zehner darin enthalten sind. Auch hier wiederrum: In einer Schleife solange 10 abziehen, bis das Ergebnis nagativ wurde. Konkret geht das 2 mal gut, also muss das nächste auszugebende Zeichen ein &#039;2&#039; sein. Damit verbleiben noch die Einer, welche direkt in das entsprechende Zeichen umgewandelt werden können. In Summe hat man also an das Display die Zeichen &#039;1&#039; &#039;2&#039; &#039;3&#039; ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number:&lt;br /&gt;
           push  temp2            ; die Funktion verändert temp2, also sichern&lt;br /&gt;
                                  ; wir den Inhalt, um ihn am Ende wieder&lt;br /&gt;
                                  ; herstellen zu können&lt;br /&gt;
&lt;br /&gt;
           mov   temp2, temp1     ; das Register temp1 frei machen&lt;br /&gt;
                                  ; abzählen wieviele Hunderter&lt;br /&gt;
                                  ; in der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
           subi  temp2, 100       ; 100 abziehen&lt;br /&gt;
           brcs  lcd_number_2     ; ist dadurch ein Unterlauf entstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Hunderter mehr ...&lt;br /&gt;
           rjmp  lcd_number_1     ; ... und ab zur nächsten Runde&lt;br /&gt;
;&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorherhgehende Schleife 100 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; abzählen wieviele Zehner in&lt;br /&gt;
                  ; der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_3:&lt;br /&gt;
           subi  temp2, 10        ; 10 abziehen&lt;br /&gt;
           brcs  lcd_number_4     ; ist dadurch ein Unterlauf enstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Zehner mehr ...&lt;br /&gt;
           rjmp  lcd_number_3     ; ... und ab zur nächsten Runde&lt;br /&gt;
&lt;br /&gt;
                              ; die Zehnerstelle ausgeben&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; die übrig gebliebenen Einer&lt;br /&gt;
                  ; noch ausgeben&lt;br /&gt;
           ldi   temp1, &#039;0&#039;       ; die Zahl in temp2 ist jetzt im Bereich&lt;br /&gt;
           add   temp1, temp2     ; 0 bis 9. Einfach nur den ASCII Code für&lt;br /&gt;
           rcall lcd_data         ; &#039;0&#039; dazu addieren und wir erhalten dierekt&lt;br /&gt;
                                  ; den ASCII Code für die Ziffer&lt;br /&gt;
&lt;br /&gt;
           pop   temp2            ; den gesicherten Inhalt von temp2 wieder herstellen&lt;br /&gt;
           ret                    ; und zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beachte: Diese Funktion benutzt wiederrum die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039;. Anders als bei den bisherigen Aufrufen ist &#039;&#039;&#039;lcd_number&#039;&#039;&#039; aber darauf angewiesen, dass &#039;&#039;&#039;lcd_data&#039;&#039;&#039; das Register &#039;&#039;&#039;temp2&#039;&#039;&#039; unangetastet lässt. Falls sie es noch nicht getan haben, dann ist das jetzt die perfekte Gelegenheit, &#039;&#039;&#039;lcd_data&#039;&#039;&#039; mit den entsprechenden &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; Befehlen zu versehen. Sie sollten dies unbedingt zur Übung selbst machen. Am Ende muß die Funktion dann wie diese hier aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1           ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap  temp1                  ; Vertauschen&lt;br /&gt;
           andi  temp1, 0b00001111      ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr   temp1, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp1        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi  temp2, 0b00001111      ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr   temp2, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp2        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur ohne RS zu setzen&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           andi  temp1, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi  temp2, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz zur Funktionsweise der Funktion &#039;&#039;&#039;lcd_number&#039;&#039;&#039;: Die Zahl in einem Register bewegt sich im Wertebereich 0 bis 255. Um herauszufinden, wie die Hunderterstelle lautet, zieht die Funktion einfach in einer Schleife immer wieder 100 von der Schleife ab, bis bei der Subtraktion ein Unterlauf, angezeigt durch das Setzen des Carry-Bits bei der Subtraktion, entsteht. Die Anzahl wird im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; mitgezählt. Da dieses Register mit dem ASCII Code von &#039;0&#039; initialisiert wurde, und dieser ASCII Code bei jedem Schleifendurchlauf um 1 erhöht wird, können wir das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; direkt zur Ausgabe des Zeichens für die Hunderterstelle durch die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; benutzen. Völlig analog funktioniert auch die Ausgabe der Zehnerstelle.&lt;br /&gt;
&lt;br /&gt;
===Unterdrückung von führenden Nullen===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung: Diese Routine ist fehlerhaft&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt jede Zahl im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; immer mit 3 Stellen aus. Führende Nullen werden nicht unterdrückt. Möchte man dies ändern, so ist das ganz leicht möglich: Vor Ausgabe der Hunderterstelle bzw. Zehnerstelle muss lediglich überprüft werden, ob die Entsprechende Ausgabe eine &#039;0&#039; wäre. Ist sie das, so wird die Ausgabe übersprungen. Lediglich in der Einerstelle wird jede Ziffer wie errechnet ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ...&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben, wenn&lt;br /&gt;
                                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_2a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_2a:&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&lt;br /&gt;
           ...&lt;br /&gt;
                              ; die Zehnerstelle ausgeben, wenn&lt;br /&gt;
                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_4a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_4a:&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Verfahren, die einzelnen Stellen durch Subtraktion zu bestimmen, ist bei kleinen Zahlen eine durchaus gängige Alternative. Vor allem dann, wenn keine hardwaremäßige Unterstützung für Multiplikation und Division zur Verfügung steht. Ansonsten könnte man die die einzelnen Ziffern auch durch Division bestimmen. Das Prinzip ist folgendes (beispielhaft an der Zahl 52783 gezeigt)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   52783 / 10          -&amp;gt; 5278&lt;br /&gt;
   52783 - 5278 * 10   -&amp;gt;          3&lt;br /&gt;
&lt;br /&gt;
   5278 / 10           -&amp;gt; 527&lt;br /&gt;
   5278 - 527 * 10     -&amp;gt;          8&lt;br /&gt;
&lt;br /&gt;
   527 / 10            -&amp;gt; 52&lt;br /&gt;
   527 - 52 * 10       -&amp;gt;          7&lt;br /&gt;
&lt;br /&gt;
   52 / 10             -&amp;gt; 5&lt;br /&gt;
   52 - 5 * 10         -&amp;gt;          2&lt;br /&gt;
&lt;br /&gt;
   5 / 10              -&amp;gt; 0&lt;br /&gt;
   5 - 0 * 10          -&amp;gt;          5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Prinzip ist also die Restbildung bei einer fortgesetzten Division durch 10, wobei die einzelnen Ziffern in umgekehrter Reihenfolge ihrer Wertigkeit entstehen. Dadurch hat man aber ein Problem: Damit die Zeichen in der richtigen Reihenfolge ausgegeben werden können, muß man sie meistens zwischenspeichern um sie in der richtigen Reihenfole ausgeben zu können. Wird die Zahl in einem Feld von immer gleicher Größe ausgegeben, dann kann man auch die Zahl von rechts nach links ausgeben (bei einem LCD ist das möglich).&lt;br /&gt;
&lt;br /&gt;
===Hexadezimal ausgeben===&lt;br /&gt;
&lt;br /&gt;
Zu guter letzt hier noch eine Funktion, die eine Zahl aus dem Register &#039;&#039;&#039;temp1&#039;&#039;&#039; in hexadezimaler Form ausgibt. Die Funktion weist keine Besonderheiten auf und sollte unmittelbar verständlich sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen hexadezimal ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number_hex:&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           rcall lcd_number_hex_digit&lt;br /&gt;
           swap  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_number_hex_digit:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
           andi  temp1, $0F&lt;br /&gt;
           cpi   temp1, 10&lt;br /&gt;
           brlt  lcd_number_hex_digit_1&lt;br /&gt;
           subi  temp1, -( &#039;A&#039; - &#039;9&#039; - 1 ) ; es wird subi mit negativer Konstante verwendet, weil es kein addi gibt&lt;br /&gt;
lcd_number_hex_digit_1:&lt;br /&gt;
           subi  temp1, -&#039;0&#039;               ; ditto&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           &lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Eine 16-Bit Zahl aus einem Registerpärchen ausgeben===&lt;br /&gt;
&lt;br /&gt;
Um eine 16 Bit Zahl auszugeben wird wieder das bewährte Schema benutzt die einzelnen Stellen durch Subtraktion abzuzählen. Da es sich hierbei allerdings um eine 16 Bit Zahl handelt, müssen die Subtraktionen als 16-Bit Arithmetik ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 16 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp2 (low Byte) / temp3 (high Byte)&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number16:&lt;br /&gt;
           push  temp1&lt;br /&gt;
           push  temp2&lt;br /&gt;
           push  temp3&lt;br /&gt;
&lt;br /&gt;
; die Zehntausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number0:&lt;br /&gt;
           subi  temp2, low(10000)&lt;br /&gt;
           sbci  temp3, high(10000)&lt;br /&gt;
           brcs  lcd_number1&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number0&lt;br /&gt;
&lt;br /&gt;
; .. und ausgeben&lt;br /&gt;
lcd_number1:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-10000)&lt;br /&gt;
           sbci  temp3, high(-10000)&lt;br /&gt;
&lt;br /&gt;
; die Tausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number2:&lt;br /&gt;
           subi  temp2, low(1000)&lt;br /&gt;
           sbci  temp3, high(1000)&lt;br /&gt;
           brcs  lcd_number3&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number2&lt;br /&gt;
&lt;br /&gt;
; ... und ausgeben&lt;br /&gt;
lcd_number3:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-1000)&lt;br /&gt;
           sbci  temp3, high(-1000)&lt;br /&gt;
&lt;br /&gt;
; Als nächtes kommt die Hunderterstelle drann&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number4:&lt;br /&gt;
           subi  temp2, low(100)&lt;br /&gt;
           sbci  temp3, high(100)&lt;br /&gt;
           brcs  lcd_number5&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number4&lt;br /&gt;
&lt;br /&gt;
; und ausgeben&lt;br /&gt;
lcd_number5:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100&lt;br /&gt;
&lt;br /&gt;
; bleiben noch die Zehner&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number6:&lt;br /&gt;
           subi  temp2, 10&lt;br /&gt;
           brcs  lcd_number7&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number6&lt;br /&gt;
&lt;br /&gt;
; ausgeben ...&lt;br /&gt;
lcd_number7:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10&lt;br /&gt;
&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
           add   temp1, temp2&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
; fertig. Stack wieder aufräumen&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           pop   temp3&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Der überarbeitete, komplette Code==&lt;br /&gt;
&lt;br /&gt;
Hier also die komplett überarbeitete Version der LCD Funktionen.&lt;br /&gt;
&lt;br /&gt;
Die für die Benutzung relevanten Funktionen&lt;br /&gt;
* &#039;&#039;&#039;lcd_init&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_clear&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_home&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_data&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_flash_string&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number_hex&#039;&#039;&#039;&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; übergeben, wobei &#039;&#039;&#039;temp1&#039;&#039;&#039; vom Usercode definiert werden muss.&lt;br /&gt;
&lt;br /&gt;
[[Media:lcd-routines.asm|Download lcd-routines.asm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Stack|&lt;br /&gt;
zurücklink=AVR-Tutorial: Stack|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Interrupts|&lt;br /&gt;
vorlink=AVR-Tutorial: Interrupts}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister SREG) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register temp1 übergeben, wobei temp1 vom Usercode definiert werden muss. &lt;br /&gt;
&lt;br /&gt;
der progger hat vergessen das Z reg in den sub&#039;s lcd_flash_string ...&lt;br /&gt;
zu sichern, aber danke für den Einstig&lt;br /&gt;
&lt;br /&gt;
Lars&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27988</id>
		<title>AVR-Tutorial: LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=27988"/>
		<updated>2008-05-16T02:33:08Z</updated>

		<summary type="html">&lt;p&gt;195.233.250.6: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kaum ein elektronisches Gerät kommt heutzutage noch ohne ein LCD daher. Ist doch auch praktisch, Informationen im Klartext anzeigen zu können, ohne irgendwelche LEDs blinken zu lassen. Kein Wunder, dass die häufigste Frage in Mikrocontroller-Foren ist: &amp;quot;Wie kann ich ein LCD anschließen?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780|&#039;&#039;&#039;HD44780&#039;&#039;&#039;]] oder einen kompatiblen (z.B. KS0070) und haben 14 oder 16 Pins. Die Pinbelegung ist praktisch immer gleich: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table width=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th width=&amp;quot;50&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Pin #&amp;lt;/th&amp;gt;&amp;lt;th  width=&amp;quot;70&amp;quot; align=&amp;quot;left&amp;quot;&amp;gt;Bezeichnung&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Funktion&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vss&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GND&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vcc&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5V&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;3&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Vee&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kontrastspannung (0V bis 5V)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;4&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Register Select (Befehle/Daten)&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;5&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RW&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Read/Write&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;6&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Enable&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;7&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 0&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;8&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 1&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;9&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 2&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;10&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 3&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;11&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 4&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;12&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;13&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 6&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;14&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DB7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Datenbit 7&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt; &lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;15&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Anode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039;&#039;&#039;16&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LED-Beleuchtung, Kathode&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine, ansonsten im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller verdreht beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert, ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt ob das LCD den vorhergehenden Befehl fertig verarbeitetet hat. Ist RS auf Daten eingestellt, dann kann man z.B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt da wir wissen, welche Anschlüsse das LCD benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Pinnummer&amp;lt;BR&amp;gt;LCD || Bezeichnung || Anschluss&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |1 || Vss || GND&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |2 || Vcc || 5V&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |3 || Vee || GND oder [[Potentiometer | Poti]] &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |4 || RS || PD4 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |5 || RW || GND &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |6 || E || PD5 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |7 || DB0 || nicht angeschlossen &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |8 || DB1 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |9 || DB2 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |10 || DB3 || nicht angeschlossen&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |11 || DB4 || PD0 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |12 || DB5 || PD1 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |13 || DB6 || PD2 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |14 || DB7 || PD3 am AVR&lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |15 || A || Vorsicht! Meistens nicht direkt an +5V anschliessbar,&amp;lt;BR&amp;gt;nur über einen Vorwiderstand! (z.B. 33&amp;amp;Omega;) &lt;br /&gt;
|-&lt;br /&gt;
| align=&amp;quot;center&amp;quot; |16 || K || GND&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Ok alles ist verbunden. Wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Doch wie bekommt man jetzt die Befehle und Daten in das Display?&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung des LCDs im 4-Bit-Modus ==&lt;br /&gt;
&lt;br /&gt;
Um ein Byte zu übertragen muss man es erstmal in die beiden Nibbles zerlegen, die getrennt übertragen werden. Da das obere Nibble (Bit 4 - Bit 7) als erstes übertragen wird, die 4 Datenleitungen jedoch an die vier unteren Bits des Port D angeschlossen sind, muss man die beiden Nibbles des zu übertragenden Bytes erstmal vertauschen. Der AVR kennt dazu praktischerweise einen eigenen Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           swap r16               ; vertauscht die beiden Nibbles von r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus 0b00100101 wird so z.B. 0b01010010. &lt;br /&gt;
&lt;br /&gt;
Jetzt sind die Bits für die erste Phase der Übertragung an der richtigen Stelle. Trotzdem wollen wir das Ergebnis nicht einfach so mit &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; an den Port geben. Um die Hälfte des Bytes, die jetzt nicht an die Datenleitungen des LCDs gegeben wird auf null zu setzen, verwendet man folgenden Befehl: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           andi r16, 0b00001111   ; Nur die vier unteren (mit 1 markierten)&lt;br /&gt;
                                  ; Bits werden übernommen, alle anderen werden null&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also: Das obere Nibble wird erst mit dem unteren vertauscht damit es unten ist, dann wird das obere (das wir jetzt noch nicht brauchen) auf null gesetzt. &lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem LCD noch mitteilen, ob wir Daten oder Befehle senden wollen. Das machen wir, indem wir das Bit an dem RS angeschlossen ist (PD4) auf 0 (= Befehl senden) oder auf 1 setzen (= Daten senden). Um ein Bit in einem normalen Register zu setzen gibt es den Befehl sbr (Set Bit in Register). Dieser Befehl unterscheidet sich jedoch von sbi (das nur für IO-Register gilt) dadurch, dass man nicht die Nummer des zu setzenden Bits angibt, sondern eine Bitmaske. Das geht so: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbr r16, 0b00010000     ; Bit 4 setzen, alle anderen Bits bleiben gleich&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An PD4 ist RS angeschlossen, wenn wir r16 an den Port D ausgeben ist RS jetzt also high und das LCD erwartet Daten anstatt von Befehlen. &lt;br /&gt;
&lt;br /&gt;
Das Ergebnis können wir jetzt endlich direkt an den Port D übergeben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           out PORTD, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss vorher der Port D auf Ausgang geschalten werden, indem man 0xFF ins Datenrichtungsregister DDRD schreibt. &lt;br /&gt;
&lt;br /&gt;
Um dem LCD zu signalisieren, dass es das an den Datenleitungen anliegende Nibble übernehmen kann, wird die E-Leitung (Enable, an PD5 angeschlossen) auf high und kurz darauf wieder auf low gesetzt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           sbi PORTD, 5              ; Enable high&lt;br /&gt;
           nop                       ; 3 Taktzyklen warten (&amp;quot;nop&amp;quot; = nichts tun)&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5              ; Enable wieder low&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die eine Hälfte des Bytes wäre damit geschafft! Die andere Hälfte kommt direkt hinterher: alles was an der obenstehenden Vorgehensweise geändert werden muss ist, das &amp;quot;swap&amp;quot; (Vertauschen der beiden Nibbles) wegzulassen.&lt;br /&gt;
&lt;br /&gt;
== Initialisierung des Displays ==&lt;br /&gt;
&lt;br /&gt;
Allerdings gibt es noch ein Problem. Wenn ein LCD eingeschaltet wird, dann läuft es zunächst im 8 Bit Modus. Irgendwie muss das Display initialisiert und auf den 4 Bit Modus umgeschaltet werden, und zwar nur mit den 4 zur Verfügung stehenden Datenleitungen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn es Probleme gibt, dann meistens an diesem Punkt. Die &amp;quot;kompatiblen&amp;quot; Kontroller sind gelegentlich doch nicht 100% identisch. Es lohnt sich das Datenblatt (siehe Weblinks im Artikel [[LCD]]) genau zu lesen, in welcher Reihenfolge und mit welchen Abständen (Delays) die Initialisierungbefehle gesendet werden. Eine weitere Hilfe können Ansteuerungsbeispiele in Forenbeiträgen geben z.B.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/79609#664268 (A) KS0066U oder Ähnliche --- LCD Treiber]&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 4 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Achtung: Im folgenden sind alle Bytes aus Sicht des LCD-Kontrollers angegeben! Da LCD-seitig nur die Leitungen DB4 - DB7 verwendet werden, ist daher immer nur das höherwertige Nibbel gültig. Durch die Art der Verschaltung (DB4 - DB7 wurde auf dem PORT an PD0 bis PD3 angeschlossen) ergibt sich dadurch eine Verschiebung, so dass das am Kontroller auszugebende Byte nibblemässig vertauscht ist!&lt;br /&gt;
&lt;br /&gt;
Die Sequenz, aus Sicht des Kontrollers, sieht so aus:&lt;br /&gt;
&lt;br /&gt;
* nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 4.1ms warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 100µs warten&lt;br /&gt;
* $3 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* $2 ins Steuerregister schreiben (RS = 0), dadurch wird auf 4 Bit Daten umstellt&lt;br /&gt;
* ab jetzt muss für die Übertragung eines Bytes jeweils das höherwertige Nibble und dann das niederwertige Nibble übertragen werden, wie oben beschrieben&lt;br /&gt;
* Mit dem Konfigurier-Befehl $20 das Display konfigurieren (4-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
=== Initialisierung im 8 Bit Modus ===&lt;br /&gt;
&lt;br /&gt;
Der Vollständigkeit halber hier noch die notwendige Initialiserungssequenz für den 8 Bit Modus. Da hier die Daten komplett als 1 Byte übertragen werden können, sind einige Klimmzüge wie im 4 Bit Modus nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
* nach dem Anlegen der Betriebsspannung muss eine Zeit von mindestens ca. 15ms gewartet werden, um dem LCD-Kontroller Zeit für seine eigene Initialisierung zu geben&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 4.1ms warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* mindestens 100µs warten&lt;br /&gt;
* $30 ins Steuerregister schreiben (RS = 0)&lt;br /&gt;
* Mit dem Konfigurier-Befehl 0x30 das Display konfigurieren (8-Bit, 1 oder 2 Zeilen, 5x7 Format)&lt;br /&gt;
* mit den restlichen Konfigurierbefehlen die Konfiguration vervollständigen: Display ein/aus, Cursor ein/aus, etc.&lt;br /&gt;
&lt;br /&gt;
== Routinen zur LCD-Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
Die Routinen zur Kommunikation mit dem LCD sehen also so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000 (Anm.1)&lt;br /&gt;
           out PORTD, temp1             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;4              ; entspricht 0b00010000&lt;br /&gt;
           out PORTD, temp2             ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out PORTD, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
 ;&lt;br /&gt;
 ; Bei höherem Takt (&amp;gt;= 8 MHz) kann es notwendig sein, &lt;br /&gt;
 ; vor dem Enable High 1-2 Wartetakte (nop) einzufügen. &lt;br /&gt;
 ; Siehe dazu http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi PORTD, 5                 ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi PORTD, 5                 ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
&lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi  temp3,50&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall  delay5ms&lt;br /&gt;
           dec  temp3&lt;br /&gt;
           brne powerupwait&lt;br /&gt;
           ldi temp1, 0b00000011        ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out PORTD, temp1             ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00000010        ; 4bit-Modus einstellen&lt;br /&gt;
           out PORTD, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi temp1, 0b00101000        ; 4Bit / 2 Zeilen / 5x8&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00001100        ; Display ein / Cursor aus / kein Blinken&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi temp1, 0b00000100        ; inkrement / kein Scrollen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi temp1, 0b00000001   ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi temp1, 0b00000010   ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anm.1: Siehe [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
Weitere Funktionen (wie z.B. Cursorposition verändern) sollten mit Hilfe der [[AVR-Tutorial:_LCD#Welche_Befehle_versteht_das_LCD.3F|Befehlscodeliste]] nicht schwer zu realisieren sein. Einfach den Code in temp laden, lcd_command aufrufen und ggf. eine Pause einfügen. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man die LCD-Ansteuerung auch an einen anderen Port des Mikrocontrollers &amp;quot;verschieben&amp;quot;: Wenn das LCD z.B. an Port B angeschlossen ist, dann reicht es im Programm alle &amp;quot;PORTD&amp;quot; durch &amp;quot;PORTB&amp;quot; und &amp;quot;DDRD&amp;quot; durch &amp;quot;DDRB&amp;quot; zu ersetzen. &lt;br /&gt;
&lt;br /&gt;
Wer eine höhere Taktfrequenz als 4 MHz verwendet, der sollte daran denken die Dauer der Verzögerungsschleifen anzupassen.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
&lt;br /&gt;
Ein Programm, das diese Routinen zur Anzeige von Text verwendet, kann z.B. so aussehen (die Datei lcd-routines.asm muss sich im gleichen Verzeichnis befinden). Nach der Initialisierung wird zuerst der Displayinhalt gelöscht. Um dem LCD ein Zeichen zu schicken, lädt man es in temp1 und ruft die Routine &amp;quot;lcd_data&amp;quot; auf. Das folgende Beispiel zeigt das Wort &amp;quot;Test&amp;quot; auf dem LCD an. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/lcd-test.asm Download lcd-test.asm] &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;
&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, 0xFF    ; Port D = Ausgang&lt;br /&gt;
           out DDRD, temp1&lt;br /&gt;
&lt;br /&gt;
           rcall lcd_init     ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear    ; Display löschen&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;T&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;e&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           &lt;br /&gt;
           ldi temp1, &#039;s&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
           ldi temp1, &#039;t&#039;     ; Zeichen anzeigen&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD-Routinen werden hier eingefügt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für längere Texte ist die Methode, jedes Zeichen einzeln in das Register zu laden und &amp;quot;lcd_data&amp;quot; aufzurufen natürlich nicht sehr praktisch. Dazu später aber mehr.&lt;br /&gt;
&lt;br /&gt;
Bisher wurden in Register immer irgendwelche Zahlenwerte geladen, aber in diesem Programm kommt plötzlich die Anweisung&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
vor. Wie ist diese zu verstehen? Passiert hier etwas grundlegend anderes als beim Laden einer Zahl in ein Register?&lt;br /&gt;
&lt;br /&gt;
Die Antwort darauf lautet: Nein. Auch hier wird letztendlich nur eine Zahl in ein Register geladen. Der Schlüssel zum Verständnis beruht darauf, dass zum LCD, so wie zu allen Ausgabegeräten, für die Ausgabe von Texten immer nur Zahlen übertragen werden, sog. Codes. Zum Beispiel könnte man vereinbaren, dass ein LCD, wenn es den Ausgabecode 65 erhält ein &#039;A&#039; anzeigt, bei einem Ausgabecode von 66 ein &#039;B&#039; usw. Naturgemäß gibt es daher viele verschiedene Code-Buchstaben Zuordnungen. Damit hier etwas Ordnung in das potentielle Chaos kommt, hat man sich bereits in der Steinzeit der Programmierung auf bestimmte Codetabellen geeinigt, von denen die verbreitetste sicherlich die ASCII-Zuordnung ist.&lt;br /&gt;
&lt;br /&gt;
==ASCII==&lt;br /&gt;
&lt;br /&gt;
ASCII steht für &#039;&#039;American Standard Code for Information Interchange&#039;&#039; und ist ein standardisierter Code zu Zeichenumsetzung. Die Codetabelle sieht hexadezimal dabei wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;.0&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x1&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x2&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x3&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x4&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x5&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x6&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x7&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x8&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;x9&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xA&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xB&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xC&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xD&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xE&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;xF&amp;lt;/th&amp;gt;&amp;lt;br&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;&#039;&#039;&#039;0x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NUL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SOH&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;STX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETX&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EOT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ENQ&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ACK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BEL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;BS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;HT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;LF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;VT&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FF&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CR&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SO&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SI&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;&#039;&#039;&#039;1x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DLE&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC2&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DC4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;NAK&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SYN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ETB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;CAN&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;EM&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SUB&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;ESC&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;FS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;GS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RS&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;US&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;&#039;&#039;&#039;2x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;SP&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;!&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;#&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;%&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;amp;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;(&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;)&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;*&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;+&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;,&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;.&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;3x&#039;&#039;&#039;&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;&amp;lt;td&amp;gt;3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;6&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;7&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;9&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;=&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;gt;&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;4x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;A&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;B&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;C&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;D&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;E&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;F&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;G&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;H&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;I&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;J&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;K&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;L&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;M&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;N&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;O&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;&#039;&#039;&#039;5x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;P&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;R&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;S&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;T&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;U&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;V&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;W&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;X&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;[&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;\&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;]&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;^&amp;lt;/td&amp;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;td&amp;gt;&#039;&#039;&#039;6x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;`&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;a&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;b&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;c&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;d&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;e&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;f&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;g&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;h&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;i&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;j&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;k&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;l&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;m&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;n&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;o&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;&#039;&#039;&#039;7x&#039;&#039;&#039;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;p&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;q&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;r&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;s&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;t&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;u&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;v&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;w&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;y&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;z&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;{&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;|&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;~&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;DEL&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 ersten beiden Zeilen enthalten die Codes für einige Steuerzeichen, ihre vollständige Beschreibung würde hier zu weit führen. Das Zeichen &#039;&#039;&#039;SP&#039;&#039;&#039; steht für ein &#039;&#039;Space&#039;&#039;, also ein Leerzeichen. &#039;&#039;&#039;BS&#039;&#039;&#039; steht für &#039;&#039;Backspace&#039;&#039;, also ein Zeichen zurück. &#039;&#039;&#039;DEL&#039;&#039;&#039; steht für &#039;&#039;Delete&#039;&#039;, also das Löschen eines Zeichens. &#039;&#039;&#039;CR&#039;&#039;&#039; steht für &#039;&#039;Carriage Return&#039;&#039;, also wörtlich: der Wagenrücklauf (einer Schreibmaschine), während &#039;&#039;&#039;LF&#039;&#039;&#039; für &#039;&#039;Line feed&#039;&#039;, also einen Zeilenvorschub steht.&lt;br /&gt;
&lt;br /&gt;
Der Assembler kennt diese Codetabelle und ersetzt die Zeile&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, &#039;T&#039;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi temp1, $54&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
was letztendlich auch der Lesbarkeit des Programmes zugute kommt. Funktional besteht kein Unterschied zwischen den beiden Anweisungen. Beide bewirken, dass das Register temp1 mit dem Bitmuster 01010100 ( = hexadezimal 54, = dezimal 84 oder eben der ASCII Code für &#039;&#039;&#039;T&#039;&#039;&#039;) geladen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das LCD wiederrum kennt diese Code-Tabelle ebenfalls und wenn es über den Datenbus die Codezahl $54 zur Anzeige empfängt, dann schreibt es ein &#039;&#039;&#039;T&#039;&#039;&#039; an die aktuelle Cursorposition. Genauer gesagt, weiss das LCD nichts von einem &#039;&#039;&#039;T&#039;&#039;&#039;. Es sieht einfach in seinen internen Tabellen nach, welche Pixel beim Empfang der Codezahl $54 auf schwarz zu setzen sind. &#039;Zufällig&#039; sind das genau jene Pixel, die für uns Menschen ein &#039;&#039;&#039;T&#039;&#039;&#039; ergeben.&lt;br /&gt;
&lt;br /&gt;
==Welche Befehle versteht das LCD?==&lt;br /&gt;
&lt;br /&gt;
Auf dem LCD arbeitet ein Kontroller vom Typ HD44780. Diesen Kontroller versteht eine Reihe von Befehlen, die allesamt mittels lcd_command gesendet werden können. Ein Kommando ist dabei nichts anderes als ein Befehlsbyte, indem die verschiedenen Bits verschiedene Bedeutung besitzen:&lt;br /&gt;
&amp;lt;table&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 0 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;dieses Bit muss 1 sein&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;x&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;der Zustand dieses Bits ist egal&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;sonstige Buchstaben&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;das Bit muss je nach gewünschter Funktionalität gesetzt werden. Die mögliche Funktionalität des jeweiligen Bits geht aus der Befehlsbeschreibung hervor&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Kommando &#039;ON/OFF Control&#039; soll benutzt werden um das Display einzuschalten, der Cursor soll eingeschaltet werden und der Cursor soll blinken.&lt;br /&gt;
Das Befehlsbyte ist so aufgebaut:&lt;br /&gt;
   0b00001dcb&lt;br /&gt;
Aus der Befehlsbeschreibung entnimmt man:&lt;br /&gt;
* Display ein bedeutet, dass an der Bitposition d eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor ein bedeutet, dass an der Bitposition c ein 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
* Cursor blinken bedeutet, dass an der Bitposition b eine 1 stehen muss.&amp;lt;br&amp;gt;&lt;br /&gt;
Das dafür zu übertragende Befehlsbyte hat also die Gestalt 0b00001111 oder in hexadezimaler Schreibweise $0F&lt;br /&gt;
&lt;br /&gt;
===Clear display: 0b00000001===&lt;br /&gt;
&lt;br /&gt;
Die Anzeige wird gelöscht und der Ausgabecursor kehrt an die Home Position (links, erste Zeile) zurück&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Cursor home: 0b0000001x===&lt;br /&gt;
&lt;br /&gt;
Der Cursor kehrt an die Home Position (links, erste Zeile) zurück. Ein verschobenes Display wird auf die Grundeinstellung zurückgesetzt.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs bis 1.64ms&lt;br /&gt;
&lt;br /&gt;
===Entry mode: 0b000001is===&lt;br /&gt;
&lt;br /&gt;
Legt die Cursor Richtung fest sowie eine mögliche Verschiebung des Displays fest&lt;br /&gt;
* i = 1, Cursorposition bei Ausgabe eines Zeichens erhöhen&lt;br /&gt;
* i = 0, Cursorposition bei Ausgabe eines Zeichens vermindern&lt;br /&gt;
* s = 1, Display wird gescrollt, wenn der Cursor das Ende/Anfang, je nach Einstellung von i, erreicht hat.&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===On/off control: 0b00001dcb===&lt;br /&gt;
&lt;br /&gt;
Display insgesamt ein/ausschalten; den Cursor ein/ausschalten; den Cursor auf blinken schalten/blinken aus. Wenn das Display ausgeschaltet wird, geht der Inhalt des Displays nicht verloren. Der vorher angezeigte Text wird nach wiedereinschalten erneut angezeigt.&lt;br /&gt;
Ist der Cursor eingeschaltet, aber Blinken ausgeschaltet, so wird der Cursor als Cursorzeile in Pixelzeile 8 dargestellt. Ist Blinken eingeschaltet, wird der Cursor als blinkendes ausgefülltes Rechteck dargestellt, welches abwechselnd mit dem Buchstaben an dieser Stelle angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
* d = 0, Display aus&lt;br /&gt;
* d = 1, Display ein&lt;br /&gt;
* c = 0, Cursor aus&lt;br /&gt;
* c = 1, Cursor ein&lt;br /&gt;
* b = 0, Cursor blinken aus&lt;br /&gt;
* b = 1, Cursor blinken ein&lt;br /&gt;
 &lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Cursor/Scrollen: 0b0001srxx===&lt;br /&gt;
&lt;br /&gt;
Bewegt den Cursor oder scrollt das Display um eine Position entweder nach rechts oder nach links.&lt;br /&gt;
&lt;br /&gt;
* s = 1, Display scrollen&lt;br /&gt;
* s = 0, Cursor bewegen&lt;br /&gt;
* r = 1, nach rechts&lt;br /&gt;
* r = 0, nach links &lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Konfiguration: 0b001dnfxx===&lt;br /&gt;
&lt;br /&gt;
Einstellen der Interface Art, Modus, Font&lt;br /&gt;
* d = 0, 4-Bit Interface&lt;br /&gt;
* d = 1, 8-Bit Interface&lt;br /&gt;
* n = 0, 1 zeilig&lt;br /&gt;
* n = 1, 2 zeilig&lt;br /&gt;
* f = 0, 5x7 Pixel&lt;br /&gt;
* f = 1, 5x11 Pixel&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Character RAM Address Set: 0b01aaaaaa===&lt;br /&gt;
&lt;br /&gt;
Mit diesem Kommando werden maximal 8 selbst definierte Zeichen definiert. Dazu wird der Character RAM Zeiger auf den Anfang des Character Generator (CG) RAM gesetzt und das Zeichen durch die Ausgabe von 8 Byte definiert. Der Adress Zeiger wird nach Ausgabe jeder Pixelzeile (8Bit) vom LCD selbst erhöht. Nach Beendigung der Zeichendefinition muss die Schreibposition explizit mit dem Kommando &amp;quot;Display RAM Address Set&amp;quot; wieder in den DD-RAM Bereich gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
aaaaaa 6-bit CG RAM Adresse&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
===Display RAM Address Set: 0b1aaaaaaa===&lt;br /&gt;
&lt;br /&gt;
Den Cursor neu positionieren. Display Data (DD) Ram ist vom Character Generator (CG) Ram unabhängig. Der Adresszeiger wird bei Ausgabe eines Zeichens ins DD Ram automatisch erhöht. Das Display verhält sich so, als ob eine Zeile immer aus 32 logischen Zeichen besteht, von der, je nach konkretem Displaytyp (16 Zeichen, 20 Zeichen) immer nur ein Teil sichtbar ist.&lt;br /&gt;
&lt;br /&gt;
aaaaaaa 7-bit DD RAM Adresse. Auf 2-zeiligen Displays (und den meisten 16x1 Displays), kann die Adressangabe wie folgt interpretiert werden&lt;br /&gt;
&lt;br /&gt;
1laaaaaa&lt;br /&gt;
* l = Zeilennummer (0 oder 1)&lt;br /&gt;
* a = 6-Bit Spaltennummer&lt;br /&gt;
&lt;br /&gt;
Ausführungszeit: 40µs&lt;br /&gt;
&lt;br /&gt;
==Einschub: Code aufräumen==&lt;br /&gt;
&lt;br /&gt;
Es wird Zeit sich einmal etwas kritisch mit den bisher geschriebenen Funktionen auseinander zu setzen.&lt;br /&gt;
&lt;br /&gt;
===Portnamen aus dem Code herausziehen===&lt;br /&gt;
&lt;br /&gt;
Wenn wir die LCD-Funktionen einmal genauer betrachten, dann fällt sofort auf, daß über die Funktionen verstreut immer wieder das &#039;&#039;&#039;PORTD&#039;&#039;&#039; sowie einzelne Zahlen für die Pins an diesem Port auftauchen. Wenn das LCD an einem anderen Port betrieben werden soll, oder sich die Pin-Belegung ändert, dann muß an all diesen Stellen eine Anpassung vorgenommen werden. Dabei darf keine einzige Stelle übersehen werden, ansonsten würden die LCD-Funktionen nicht oder nicht vollständig funktionieren.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit dem vorzubeugen ist es, diese immer gleichbleibenden Dinge an den Anfang der LCD-Funktionen vorzuziehen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;;                 LCD-Routinen                ;;&lt;br /&gt;
;;                 ============                ;;&lt;br /&gt;
;;              (c)andreas-s@web.de            ;;&lt;br /&gt;
;;                                             ;;&lt;br /&gt;
;; 4bit-Interface                              ;;&lt;br /&gt;
;; DB4-DB7:       PD0-PD3                      ;;&lt;br /&gt;
;; RS:            PD4                          ;;&lt;br /&gt;
;; E:             PD5                          ;;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
.equ PIN_E    = 5&lt;br /&gt;
.equ PIN_RS   = 4&lt;br /&gt;
&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           mov temp2, temp1             ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap temp1                   ; Vertauschen&lt;br /&gt;
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr temp1, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp1          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr temp2, 1&amp;lt;&amp;lt;PIN_RS         ; entspricht 0b00010000&lt;br /&gt;
           out LCD_PORT, temp2          ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur RS=0&lt;br /&gt;
           mov temp2, temp1&lt;br /&gt;
           swap temp1&lt;br /&gt;
           andi temp1, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi temp2, 0b00001111&lt;br /&gt;
           out LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; erzeugt den Enable-Puls&lt;br /&gt;
lcd_enable:&lt;br /&gt;
           sbi LCD_PORT, PIN_E          ; Enable high&lt;br /&gt;
           nop                          ; 3 Taktzyklen warten&lt;br /&gt;
           nop&lt;br /&gt;
           nop&lt;br /&gt;
           cbi LCD_PORT, PIN_E          ; Enable wieder low&lt;br /&gt;
           ret                          ; Und wieder zurück                     &lt;br /&gt;
 &lt;br /&gt;
 ; Pause nach jeder Übertragung&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
 &lt;br /&gt;
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden&lt;br /&gt;
lcd_init:&lt;br /&gt;
           ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf Ausgang&lt;br /&gt;
           out   LCD_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
           ldi   temp3,6&lt;br /&gt;
powerupwait:&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           dec   temp3&lt;br /&gt;
           brne  powerupwait&lt;br /&gt;
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet&lt;br /&gt;
           out   LCD_PORT, temp1        ; werden zur Initialisierung&lt;br /&gt;
           rcall lcd_enable             ; 1&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; 2&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           rcall lcd_enable             ; und 3!&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ldi   temp1, 0b00101000      ; 4 Bot, 2 Zeilen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00001100      ; Display on, Cursor off&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ldi   temp1, 0b00000100      ; endlich fertig&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           ret&lt;br /&gt;
 &lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
 ; Sendet den Befehl: Cursor Home&lt;br /&gt;
lcd_home:&lt;br /&gt;
           ldi   temp1, 0b00000010      ; Cursor Home&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels &#039;&#039;&#039;.equ&#039;&#039;&#039; werden mit dem Assembler Textersetzungen vereinbart. Der Assembler ersetzt alle Vorkomnisse des Quelltextes durch den zu ersetzenden Text. Dadurch ist es zb. möglich alle Vorkommnisse von &#039;&#039;&#039;PORTD&#039;&#039;&#039; durch &#039;&#039;&#039;LCD_PORT&#039;&#039;&#039; auszutauschen. Wird das LCD an einen anderen Port, zb. &#039;&#039;&#039;PORTB&#039;&#039;&#039; gelegt, dann genügt es, einzig und alleine die Zeilen&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTD&lt;br /&gt;
.equ LCD_DDR  = DDRD&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ LCD_PORT = PORTB&lt;br /&gt;
.equ LCD_DDR  = DDRB&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
zu ersetzen. Der Assembler sorgt dann dafür, dass diese Portänderung an den relevanten Stellen im Code über die Textersetzungen einfliesst. Selbiges natürlich mit der Pin-Zuordnung.&lt;br /&gt;
&lt;br /&gt;
===Registerbenutzung===&lt;br /&gt;
&lt;br /&gt;
Bei diesen Funktionen mussten einige Register des Prozessors benutzt werden um darin Zwischenergebnisse zu speichern bzw. zu bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss dabei natürlich, dass es zu keinen Überschneidungen kommt. Solange nur jede Funktion jeweils für sich betrachtet wird, ist das kein Problem. In 20 oder 30 Code-Zeilen kann man gut verfolgen, welches Register wofür benutzt wird. Schwieriger wird es, wenn Funktionen wiederrum andere Funktionen aufrufen, die ihrerseits wieder Funktionen aufrufen usw. Jede dieser Funktionen benutzt einige Register und mit zunehmender Programmgröße wird es immer schwieriger zu verfolgen, welches Register zu welchem Zeitpunkt wofür benutzt wird.&lt;br /&gt;
&lt;br /&gt;
Speziell bei Basisfunktionen, wie diesen LCD-Funktionen, ist es daher oft ratsam, dafür zu sorgen, daß jede Funktion die Register wieder in dem Zustand hinterlässt, indem sie sie auch vorgefunden hat. Wir benötigen dazu wieder den Stack, auf dem die Registerinhalte bei Betreten einer Funktion zwischengespeichert werden und von dem die Register bei Verlassen einer Funktion wiederhergestellt werden.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir die Funktion&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion verändert das Register temp1. Um das Register abzusichern schreiben wir die Funktion um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
lcd_clear:&lt;br /&gt;
           push  temp1                  ; temp1 auf dem Stack sichern&lt;br /&gt;
           ldi   temp1, 0b00000001      ; Display löschen&lt;br /&gt;
           rcall lcd_command&lt;br /&gt;
           rcall delay5ms&lt;br /&gt;
           pop   temp1                  ; temp1 vom Stack wiederherstellen&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am besten hält man sich an die Regel: Jede Funktion ist dafür zuständig die Register zu sichern und wiederherzustellen, die sie auch selbst verändert. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; ruft die Funktionen &#039;&#039;&#039;lcd_command&#039;&#039;&#039; und &#039;&#039;&#039;delay5ms&#039;&#039;&#039; auf. Wenn diese Funktionen selbst wieder Register verändern (und das tun sie), so ist es die Aufgabe dieser Funktionen, sich um die Sicherung und das Wiederherstellen der entsprechenden Register zu kümmern. &#039;&#039;&#039;lcd_clear&#039;&#039;&#039; sollte sich nicht darum kümmern müssen. Auf diese Weise ist das Schlimmste, das einem passieren kann, das ein paar Register unnütz gesichert und wiederhergestellt werden. Das kostet zwar etwas Rechenzeit und etwas Speicherplatz auf dem Stack, ist aber immer noch besser als das andere Extrem: Nach einem Funktionsaufruf haben einige Register nicht mehr den Wert den sie haben sollten und das Programm rechnet mit falschen Zahlen weiter.&lt;br /&gt;
&lt;br /&gt;
===Lass den Assembler rechnen===&lt;br /&gt;
Betrachtet man den Code genauer, so fallen einige konstante Zahlenwerte auf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, $42&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Das vorangestellte $ kennzeichnet die Zahl als Hexadezimalzahl) Der Code benötigt eine Warteschleife die mindestens 50µs dauert. Die beiden Befehle innerhalb der Schleife benötigen 3 Takte: 1 Takt für den &#039;&#039;&#039;dec&#039;&#039;&#039; und der &#039;&#039;&#039;brne&#039;&#039;&#039; benötigt 2 Takte wenn die Bedingung zutrifft, der Branch also genommen wird. Bei 4 Mhz werden also 4000000 / 3 * 50 / 1000000 = 66.6 Durchläufe durch die Schleife benötigt um eine Verzögerungszeit von 50µs (0.000050 Sekunden) zu erreichen, hexadezimal ausgedrückt: $42&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist: Bei anderen Taktfrequenzen müsste man nun jedesmal diese Berechnung machen und den entsprechenden Zahlenwert einsetzen. Das kann aber der Assembler genausogut erledigen. Am Anfang des Codes wird ein Eintrag definiert, der die Taktfrequenz festlegt. Traditionell heist dieser Eintrag &amp;lt;i&amp;gt;XTAL&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ XTAL  = 4000000&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
delay50us:                              ; 50us Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 50 / 3 ) / 1000000&lt;br /&gt;
delay50us_:&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne delay50us_&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An einer anderen Codestelle gibt es weitere derartige magische Zahlen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, $21&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was geht hier vor?&lt;br /&gt;
Die innere Schleife benötigt wieder 3 Takte pro Durchlauf. Bei $C9 = 201 Durchläufen werden also 201 * 3 = 603 Takte verbraucht. In der äußeren Schleife kommen pro Durchlauf alo 1 + 603 + 1 + 2 = 607 Takte verbraucht. Da die äußere Schleife $21 = 33 mal wiederholt wird, werden 20031 Takte verbraucht. Bei 4Mhz benötigt der Prozessor 20031 / 4000000 = 0.005007 Sekunden, also 5 ms.&lt;br /&gt;
Wird der Wiederholwert für die innere Schleife bei $C9 belassen, so werden 4000000 / 607 * 5 / 1000 Wiederholungen der äusseren Schleife benötigt. Auch diese Berechnung kann wieder der Assembler übernehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           ldi  temp1, ( XTAL * 5 / 607 ) / 1000&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein kleines Problem kann bei der Verwendung dieses Verfahrens entstehen: Bei hohen Taktfrequenzen und großen Wartezeiten kann der berechnete Wert größer als 255 werden und man bekommt die Fehlermeldung &amp;quot;Operand(s) out of range&amp;quot; beim Assemblieren. Dieser Fall tritt zum Beispiel für obige Konstruktion bei einer Taktfrequenz von 16 MHz ein (genauer gesagt ab 15,3 MHz), während darunter XTAL beliebig geändert werden kann. Als einfachste Lösung bietet es sich an, die Zahl der Takte pro Schleifendurchlauf durch das Einfügen von &#039;&#039;&#039;nop&#039;&#039;&#039; zu erhöhen und die Berechnungsvorschrift anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Ausgabe eines konstanten Textes ==&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde schon einmal ein Text ausgegeben. Dies geschah durch Ausgabe von einzelnen Zeichen. Das können wir auch anders machen. Wir können den Text im Speicher ablegen und eine Funktion schreiben, die die einzelnen Zeichen aus dem Speicher holt und ausgibt. Dabei erhebt sich aber eine Fragestellung: Woher weiß die Funktion eigentlich, wie lange der Text ist? Die Antwort darauf lautet: Sie kann es nicht wissen. Wir müssen irgendwelche Vereinbarungen treffen, woran die Funktion erkennen kann, dass der Text zu Ende ist. Im Wesentlichen werden dazu 2 Methoden benutzt:&lt;br /&gt;
* Der Text enthält ein spezielles Zeichen, welches das Ende des Textes markiert&lt;br /&gt;
* Wir speichern nicht nur den Text selbst, sondern auch die Länge des Textes&lt;br /&gt;
Mit einer der beiden Methoden ist es der Textausgabefunktion dann ein Leichtes, den Text vollständig auszugeben.&lt;br /&gt;
&lt;br /&gt;
Wir werden uns im Weiteren dafür entscheiden, ein spezielles Zeichen, eine 0, dafür zu benutzen. Die Ausgabefunktionen werden dann etwas einfacher, als wenn bei der Ausgabe die Anzahl der bereits ausgegebenen Zeichen mitgezählt werden muss.&lt;br /&gt;
&lt;br /&gt;
Den Text selbst speichern wir im Flash-Speicher, also dort wo auch das Programm gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ; Einen konstanten Text aus dem Flash Speicher&lt;br /&gt;
 ; ausgeben. Der Text wird mit einer 0 beendet&lt;br /&gt;
lcd_flash_string:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_1:&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           rjmp  lcd_flash_string_1&lt;br /&gt;
&lt;br /&gt;
lcd_flash_string_2:&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion benutzt den Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039; um das jeweils nächste Zeichen aus dem Flash Speicher in ein Register zur Weiterverarbeitung zu laden. Dazu wird der sog. &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; benutzt. So nennt man das Registerpaar &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;. Nach jedem Ladevorgang wird dabei durch den Befehl&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           lpm   temp1, Z+&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
dieser Z-Pointer um 1 erhöht. Mittels &#039;&#039;&#039;cpi&#039;&#039;&#039; wird das in das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; geladene Zeichen mit 0 verglichen. &#039;&#039;&#039;cpi&#039;&#039;&#039; vergleicht die beiden Zahlen und merkt sich das Ergebnis in einem speziellen Register in Form von Status Bits. &#039;&#039;&#039;cpi&#039;&#039;&#039; zieht dabei ganz einfach die beiden Zahlen voneinander ab. Sind sie gleich, so kommt da als Ergebnis 0 heraus und &#039;&#039;&#039;cpi&#039;&#039;&#039; setzt daher konsequenter Weise das Zero-Flag, das anzeigt, daß die vorhergegangene Operation ein 0 Ergebnis hatte.&#039;&#039;&#039;breq&#039;&#039;&#039; wertet diese Status-Bits aus. Wenn die vorhergegangene Operation ein 0-Ergebnis hatte, das Zero-Flag also gesetzt ist, dann wird ein Sprung zum angegebenen Label durchgeführt. In Summe bewirkt also die Sequenz&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           cpi   temp1, 0&lt;br /&gt;
           breq  lcd_flash_string_2&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
das das gelesene Zeichen mit 0 verglichen wird und falls das gelesene&lt;br /&gt;
Zeichen tatsächlich 0 war, an der Stelle lcd_flash_string_2 weiter gemacht wird. Im anderen Fall wird die bereits geschriebene Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aufgerufen, welche das Zeichen ausgibt. &#039;&#039;&#039;lcd_data&#039;&#039;&#039; erwartet dabei das Zeichen im Register &#039;&#039;&#039;temp1&#039;&#039;&#039;, genau in dem Register in welches wir vorher mittels &#039;&#039;&#039;lpm&#039;&#039;&#039; das Zeichen geladen hatten.&lt;br /&gt;
&lt;br /&gt;
Das verwendende Programm sieht dann 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 temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
           ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPL, temp1&lt;br /&gt;
           ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
           out SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
           rcall lcd_init              ; Display initialisieren&lt;br /&gt;
           rcall lcd_clear             ; Display löschen&lt;br /&gt;
 &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;
&lt;br /&gt;
           rcall lcd_flash_string      ; Unterprogramm gibt String aus der&lt;br /&gt;
                                       ; durch den Z-Pointer adressiert wird&lt;br /&gt;
loop:&lt;br /&gt;
           rjmp loop&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
           .db &amp;quot;Test&amp;quot;,0                ; Stringkonstante, durch eine 0&lt;br /&gt;
                                       ; abgeschlossen  &lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;            ; LCD Funktionen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Genaueres über die Verwendung unterschiedlicher Speicher findet sich im Kapitel [[AVR-Tutorial:_Speicher|Speicher]]&lt;br /&gt;
&lt;br /&gt;
==Zahlen ausgeben==&lt;br /&gt;
Um Zahlen, die beispielsweise in einem Register gespeichert sind, ausgeben zu können, ist es notwendig sich eine Textrepräsentierung der Zahl zu generieren. Die Zahl 123 wird also in den Text &amp;quot;123&amp;quot; umgewandelt welcher dann ausgegeben wird. Aus praktischen Gründen wird allerdings der Text nicht vollständig generiert (man müsste ihn ja irgendwo zwischenspeichern) sondern die einzelnen Buchstaben werden sofort ausgegeben, sobald sie bekannt sind.&lt;br /&gt;
&lt;br /&gt;
===Dezimal ausgeben===&lt;br /&gt;
Das Prinzip der Umwandlung ist einfach. Um herauszufinden wieviele Hunderter in der Zahl 123 enthalten sind, genügt es in einer Schleife immer wieder 100 von der Zahl abzuziehen und mitzuzählen wie oft dies gelang, bevor das Ergebnis negativ wurde. In diesem Fall lautet die Antwort: 1 mal, denn 123 - 100 macht 23. Versucht man erneut 100 anzuziehen, so ergibt sich eine negative Zahl.&lt;br /&gt;
Also muss eine &#039;1&#039; ausgeben werden. Die verbleibenden 23 werden weiterbehandelt, indem festgestellt wird wieviele Zehner darin enthalten sind. Auch hier wiederrum: In einer Schleife solange 10 abziehen, bis das Ergebnis nagativ wurde. Konkret geht das 2 mal gut, also muss das nächste auszugebende Zeichen ein &#039;2&#039; sein. Damit verbleiben noch die Einer, welche direkt in das entsprechende Zeichen umgewandelt werden können. In Summe hat man also an das Display die Zeichen &#039;1&#039; &#039;2&#039; &#039;3&#039; ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number:&lt;br /&gt;
           push  temp2            ; die Funktion verändert temp2, also sichern&lt;br /&gt;
                                  ; wir den Inhalt, um ihn am Ende wieder&lt;br /&gt;
                                  ; herstellen zu können&lt;br /&gt;
&lt;br /&gt;
           mov   temp2, temp1     ; das Register temp1 frei machen&lt;br /&gt;
                                  ; abzählen wieviele Hunderter&lt;br /&gt;
                                  ; in der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
           subi  temp2, 100       ; 100 abziehen&lt;br /&gt;
           brcs  lcd_number_2     ; ist dadurch ein Unterlauf entstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Hunderter mehr ...&lt;br /&gt;
           rjmp  lcd_number_1     ; ... und ab zur nächsten Runde&lt;br /&gt;
;&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorherhgehende Schleife 100 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; abzählen wieviele Zehner in&lt;br /&gt;
                  ; der Zahl enthalten sind&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number_3:&lt;br /&gt;
           subi  temp2, 10        ; 10 abziehen&lt;br /&gt;
           brcs  lcd_number_4     ; ist dadurch ein Unterlauf enstanden?&lt;br /&gt;
           inc   temp1            ; Nein: 1 Zehner mehr ...&lt;br /&gt;
           rjmp  lcd_number_3     ; ... und ab zur nächsten Runde&lt;br /&gt;
&lt;br /&gt;
                              ; die Zehnerstelle ausgeben&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
                              ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                  ; abgezogen hat&lt;br /&gt;
&lt;br /&gt;
                                  ; die übrig gebliebenen Einer&lt;br /&gt;
                  ; noch ausgeben&lt;br /&gt;
           ldi   temp1, &#039;0&#039;       ; die Zahl in temp2 ist jetzt im Bereich&lt;br /&gt;
           add   temp1, temp2     ; 0 bis 9. Einfach nur den ASCII Code für&lt;br /&gt;
           rcall lcd_data         ; &#039;0&#039; dazu addieren und wir erhalten dierekt&lt;br /&gt;
                                  ; den ASCII Code für die Ziffer&lt;br /&gt;
&lt;br /&gt;
           pop   temp2            ; den gesicherten Inhalt von temp2 wieder herstellen&lt;br /&gt;
           ret                    ; und zurück&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beachte: Diese Funktion benutzt wiederrum die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039;. Anders als bei den bisherigen Aufrufen ist &#039;&#039;&#039;lcd_number&#039;&#039;&#039; aber darauf angewiesen, dass &#039;&#039;&#039;lcd_data&#039;&#039;&#039; das Register &#039;&#039;&#039;temp2&#039;&#039;&#039; unangetastet lässt. Falls sie es noch nicht getan haben, dann ist das jetzt die perfekte Gelegenheit, &#039;&#039;&#039;lcd_data&#039;&#039;&#039; mit den entsprechenden &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; Befehlen zu versehen. Sie sollten dies unbedingt zur Übung selbst machen. Am Ende muß die Funktion dann wie diese hier aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 ;sendet ein Datenbyte an das LCD&lt;br /&gt;
lcd_data:&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1           ; &amp;quot;Sicherungskopie&amp;quot; für&lt;br /&gt;
                                        ; die Übertragung des 2.Nibbles&lt;br /&gt;
           swap  temp1                  ; Vertauschen&lt;br /&gt;
           andi  temp1, 0b00001111      ; oberes Nibble auf Null setzen&lt;br /&gt;
           sbr   temp1, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp1        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
                                        ; 2. Nibble, kein swap da es schon&lt;br /&gt;
                                        ; an der richtigen stelle ist&lt;br /&gt;
           andi  temp2, 0b00001111      ; obere Hälfte auf Null setzen &lt;br /&gt;
           sbr   temp2, 1&amp;lt;&amp;lt;PIN_RS       ; entspricht 0b00010000&lt;br /&gt;
           out   LCD_PORT, temp2        ; ausgeben&lt;br /&gt;
           rcall lcd_enable             ; Enable-Routine aufrufen&lt;br /&gt;
           rcall delay50us              ; Delay-Routine aufrufen&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret                          ; zurück zum Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
 ; sendet einen Befehl an das LCD&lt;br /&gt;
lcd_command:                            ; wie lcd_data, nur ohne RS zu setzen&lt;br /&gt;
           push  temp2&lt;br /&gt;
           mov   temp2, temp1&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           andi  temp1, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp1&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           andi  temp2, 0b00001111&lt;br /&gt;
           out   LCD_PORT, temp2&lt;br /&gt;
           rcall lcd_enable&lt;br /&gt;
           rcall delay50us&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kurz zur Funktionsweise der Funktion &#039;&#039;&#039;lcd_number&#039;&#039;&#039;: Die Zahl in einem Register bewegt sich im Wertebereich 0 bis 255. Um herauszufinden, wie die Hunderterstelle lautet, zieht die Funktion einfach in einer Schleife immer wieder 100 von der Schleife ab, bis bei der Subtraktion ein Unterlauf, angezeigt durch das Setzen des Carry-Bits bei der Subtraktion, entsteht. Die Anzahl wird im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; mitgezählt. Da dieses Register mit dem ASCII Code von &#039;0&#039; initialisiert wurde, und dieser ASCII Code bei jedem Schleifendurchlauf um 1 erhöht wird, können wir das Register &#039;&#039;&#039;temp1&#039;&#039;&#039; direkt zur Ausgabe des Zeichens für die Hunderterstelle durch die Funktion &#039;&#039;&#039;lcd_data&#039;&#039;&#039; benutzen. Völlig analog funktioniert auch die Ausgabe der Zehnerstelle.&lt;br /&gt;
&lt;br /&gt;
===Unterdrückung von führenden Nullen===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung: Diese Routine ist fehlerhaft&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt jede Zahl im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; immer mit 3 Stellen aus. Führende Nullen werden nicht unterdrückt. Möchte man dies ändern, so ist das ganz leicht möglich: Vor Ausgabe der Hunderterstelle bzw. Zehnerstelle muss lediglich überprüft werden, ob die Entsprechende Ausgabe eine &#039;0&#039; wäre. Ist sie das, so wird die Ausgabe übersprungen. Lediglich in der Einerstelle wird jede Ziffer wie errechnet ausgegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ...&lt;br /&gt;
                                  ; die Hunderterstelle ausgeben, wenn&lt;br /&gt;
                                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_2:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_2a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_2a:&lt;br /&gt;
           subi  temp2, -100      ; 100 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&lt;br /&gt;
           ...&lt;br /&gt;
                              ; die Zehnerstelle ausgeben, wenn&lt;br /&gt;
                  ; sie nicht &#039;0&#039; ist&lt;br /&gt;
lcd_number_4:&lt;br /&gt;
           cpi   temp1, &#039;0&#039;&lt;br /&gt;
           breq  lcd_number_4a&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
lcd_number_4a:&lt;br /&gt;
           subi  temp2, -10       ; 10 wieder dazuzählen, da die&lt;br /&gt;
           ...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Verfahren, die einzelnen Stellen durch Subtraktion zu bestimmen, ist bei kleinen Zahlen eine durchaus gängige Alternative. Vor allem dann, wenn keine hardwaremäßige Unterstützung für Multiplikation und Division zur Verfügung steht. Ansonsten könnte man die die einzelnen Ziffern auch durch Division bestimmen. Das Prinzip ist folgendes (beispielhaft an der Zahl 52783 gezeigt)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   52783 / 10          -&amp;gt; 5278&lt;br /&gt;
   52783 - 5278 * 10   -&amp;gt;          3&lt;br /&gt;
&lt;br /&gt;
   5278 / 10           -&amp;gt; 527&lt;br /&gt;
   5278 - 527 * 10     -&amp;gt;          8&lt;br /&gt;
&lt;br /&gt;
   527 / 10            -&amp;gt; 52&lt;br /&gt;
   527 - 52 * 10       -&amp;gt;          7&lt;br /&gt;
&lt;br /&gt;
   52 / 10             -&amp;gt; 5&lt;br /&gt;
   52 - 5 * 10         -&amp;gt;          2&lt;br /&gt;
&lt;br /&gt;
   5 / 10              -&amp;gt; 0&lt;br /&gt;
   5 - 0 * 10          -&amp;gt;          5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Prinzip ist also die Restbildung bei einer fortgesetzten Division durch 10, wobei die einzelnen Ziffern in umgekehrter Reihenfolge ihrer Wertigkeit entstehen. Dadurch hat man aber ein Problem: Damit die Zeichen in der richtigen Reihenfolge ausgegeben werden können, muß man sie meistens zwischenspeichern um sie in der richtigen Reihenfole ausgeben zu können. Wird die Zahl in einem Feld von immer gleicher Größe ausgegeben, dann kann man auch die Zahl von rechts nach links ausgeben (bei einem LCD ist das möglich).&lt;br /&gt;
&lt;br /&gt;
===Hexadezimal ausgeben===&lt;br /&gt;
&lt;br /&gt;
Zu guter letzt hier noch eine Funktion, die eine Zahl aus dem Register &#039;&#039;&#039;temp1&#039;&#039;&#039; in hexadezimaler Form ausgibt. Die Funktion weist keine Besonderheiten auf und sollte unmittelbar verständlich sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 8 Bit Zahl ohne Vorzeichen hexadezimal ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp1&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number_hex:&lt;br /&gt;
           swap  temp1&lt;br /&gt;
           rcall lcd_number_hex_digit&lt;br /&gt;
           swap  temp1&lt;br /&gt;
&lt;br /&gt;
lcd_number_hex_digit:&lt;br /&gt;
           push  temp1&lt;br /&gt;
&lt;br /&gt;
           andi  temp1, $0F&lt;br /&gt;
           cpi   temp1, 10&lt;br /&gt;
           brlt  lcd_number_hex_digit_1&lt;br /&gt;
           subi  temp1, -( &#039;A&#039; - &#039;9&#039; - 1 ) ; es wird subi mit negativer Konstante verwendet, weil es kein addi gibt&lt;br /&gt;
lcd_number_hex_digit_1:&lt;br /&gt;
           subi  temp1, -&#039;0&#039;               ; ditto&lt;br /&gt;
           rcall  lcd_data&lt;br /&gt;
           &lt;br /&gt;
           pop   temp1&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Eine 16-Bit Zahl aus einem Registerpärchen ausgeben===&lt;br /&gt;
&lt;br /&gt;
Um eine 16 Bit Zahl auszugeben wird wieder das bewährte Schema benutzt die einzelnen Stellen durch Subtraktion abzuzählen. Da es sich hierbei allerdings um eine 16 Bit Zahl handelt, müssen die Subtraktionen als 16-Bit Arithmetik ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************&lt;br /&gt;
;&lt;br /&gt;
; Eine 16 Bit Zahl ohne Vorzeichen ausgeben&lt;br /&gt;
;&lt;br /&gt;
; Übergabe:            Zahl im Register temp2 (low Byte) / temp3 (high Byte)&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
lcd_number16:&lt;br /&gt;
           push  temp1&lt;br /&gt;
           push  temp2&lt;br /&gt;
           push  temp3&lt;br /&gt;
&lt;br /&gt;
; die Zehntausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number0:&lt;br /&gt;
           subi  temp2, low(10000)&lt;br /&gt;
           sbci  temp3, high(10000)&lt;br /&gt;
           brcs  lcd_number1&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number0&lt;br /&gt;
&lt;br /&gt;
; .. und ausgeben&lt;br /&gt;
lcd_number1:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-10000)&lt;br /&gt;
           sbci  temp3, high(-10000)&lt;br /&gt;
&lt;br /&gt;
; die Tausenderstellen abzählen ...&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number2:&lt;br /&gt;
           subi  temp2, low(1000)&lt;br /&gt;
           sbci  temp3, high(1000)&lt;br /&gt;
           brcs  lcd_number3&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number2&lt;br /&gt;
&lt;br /&gt;
; ... und ausgeben&lt;br /&gt;
lcd_number3:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, low(-1000)&lt;br /&gt;
           sbci  temp3, high(-1000)&lt;br /&gt;
&lt;br /&gt;
; Als nächtes kommt die Hunderterstelle drann&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number4:&lt;br /&gt;
           subi  temp2, low(100)&lt;br /&gt;
           sbci  temp3, high(100)&lt;br /&gt;
           brcs  lcd_number5&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number4&lt;br /&gt;
&lt;br /&gt;
; und ausgeben&lt;br /&gt;
lcd_number5:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -100&lt;br /&gt;
&lt;br /&gt;
; bleiben noch die Zehner&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
lcd_number6:&lt;br /&gt;
           subi  temp2, 10&lt;br /&gt;
           brcs  lcd_number7&lt;br /&gt;
           inc   temp1&lt;br /&gt;
           rjmp  lcd_number6&lt;br /&gt;
&lt;br /&gt;
; ausgeben ...&lt;br /&gt;
lcd_number7:&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
           subi  temp2, -10&lt;br /&gt;
&lt;br /&gt;
           ldi   temp1, &#039;0&#039;&lt;br /&gt;
           add   temp1, temp2&lt;br /&gt;
           rcall lcd_data&lt;br /&gt;
&lt;br /&gt;
; fertig. Stack wieder aufräumen&lt;br /&gt;
           pop   temp1&lt;br /&gt;
           pop   temp2&lt;br /&gt;
           pop   temp3&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Der überarbeitete, komplette Code==&lt;br /&gt;
&lt;br /&gt;
Hier also die komplett überarbeitete Version der LCD Funktionen.&lt;br /&gt;
&lt;br /&gt;
Die für die Benutzung relevanten Funktionen&lt;br /&gt;
* &#039;&#039;&#039;lcd_init&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_clear&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_home&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_data&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_flash_string&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;lcd_number_hex&#039;&#039;&#039;&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register &#039;&#039;&#039;temp1&#039;&#039;&#039; übergeben, wobei &#039;&#039;&#039;temp1&#039;&#039;&#039; vom Usercode definiert werden muss.&lt;br /&gt;
&lt;br /&gt;
[[Media:lcd-routines.asm|Download lcd-routines.asm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Stack|&lt;br /&gt;
zurücklink=AVR-Tutorial: Stack|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Interrupts|&lt;br /&gt;
vorlink=AVR-Tutorial: Interrupts}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
sind so ausgeführt, dass sie kein Register (ausser dem Statusregister SREG) verändern. Die bei manchen Funktionen notwendige Argumente werden immer im Register temp1 übergeben, wobei temp1 vom Usercode definiert werden muss. &lt;br /&gt;
&lt;br /&gt;
der progger hat vergessen das Z reg in den sub&#039;s lcd_flash_string ...&lt;br /&gt;
zu sichern, aber danke für den Einstig&lt;br /&gt;
&lt;br /&gt;
Lars&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Stack&amp;diff=27984</id>
		<title>AVR-Tutorial: Stack</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Stack&amp;diff=27984"/>
		<updated>2008-05-16T02:18:07Z</updated>

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

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

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

		<summary type="html">&lt;p&gt;195.233.250.6: /* Division in Software */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Eine der Hauptaufgaben eines Mikrokontrollers bzw. eines Computers allgemein, ist es, irgendwelche Berechnungen anzustellen. Der Löwenanteil an den meisten Berechnungen entfällt dabei auf einfache Additionen bzw. Subtraktionen. Multiplikationen bzw. Divisionen kommen schon seltener vor, bzw. können oft durch entsprechende Additionen bzw. Subtraktionen ersetzt werden. Weitergehende mathematische Konstrukte werden zwar auch ab und an benötigt, können aber in der Assemblerprogrammierung durch geschickte Umformungen oft vermieden werden.&lt;br /&gt;
&lt;br /&gt;
= Hardwareunterstützung =&lt;br /&gt;
&lt;br /&gt;
Praktisch alle Mikroprozessoren unterstützen Addition und Subtraktion direkt in Hardware, das heißt: Sie haben eigene Befehle dafür. Einige bringen auch Unterstützung für eine Hardwaremultiplikation mit (so zum Beispiel der &amp;lt;b&amp;gt;ATMega8&amp;lt;/b&amp;gt;), während Division in Hardware schon seltener zu finden ist.&lt;br /&gt;
&lt;br /&gt;
= 8 Bit versus 16 Bit =&lt;br /&gt;
&lt;br /&gt;
In diesem Abschnitt des Tutorials wird gezielt lediglich auf 8 Bit Arithmetik eingegangen, um zunächst mal die Grundlagen des Rechnens mit einem µC zu zeigen. Die Erweiterung von 8 Bit auf 16 Bit Arithmetik ist in einigen Fällen trivial (Addition + Subtraktion), kann sich aber bei Multiplikation und Division schon in einem beträchtlichem Codeumfang niederschlagen.&lt;br /&gt;
&lt;br /&gt;
Der im Tutorial verwendete &amp;lt;b&amp;gt;ATMega8&amp;lt;/b&amp;gt; besitzt eine sog. 8-Bit Architektur. Das heißt, dass seine Rechenregister (mit Ausnahmen) nur 8 Bit breit sind und sich daher eine 8 Bit Arithmetik als die natürliche Form der Rechnerei auf diesem Prozessor anbietet. Berechnungen, die mehr als 8 Bit erfordern, müssen dann durch Kombinationen von Rechenvorgängen realisiert werden. Eine Analogie wäre z.B. das Rechnen, wie wir alle es in der Grundschule gelernt haben. Auch wenn wir in der Grundschule (in den Anfängen) nur die Additionen mit Zahlen kleiner als 10 auswendig gelernt haben, so können wir doch durch die Kombination von mehreren derartigen Additionen beliebig große Zahlen addieren. Das gleiche gilt für Multiplikationen. In der Grundschule musste wohl jeder von uns das sog. &#039;kleine Einmaleins&#039; auswendig lernen, um Multiplikationen im Zahlenraum bis 100 quasi &#039;in Hardware&#039; zu berechnen. Und doch können wir durch Kombinationen solcher Einfachmultiplikationen und zusätzlichen Additionen in beliebig große Zahlenräume vorstoßen.&lt;br /&gt;
&lt;br /&gt;
Die Einschränkung auf 8 Bit ist also keineswegs eine Einschränkung in dem Sinne, dass es eine prinzipielle Obergrenze für Berechnungen gäbe. Sie bedeutet lediglich eine obere Grenze dafür, bis zu welchen Zahlen in einem Rutsch gerechnet werden kann. Alles, was darüber hinausgeht, muss dann mittels Kombinationen von Berechnungen gemacht werden.&lt;br /&gt;
&lt;br /&gt;
= 8-Bit Arithmetik ohne Berücksichtigung eines Vorzeichens =&lt;br /&gt;
&lt;br /&gt;
Die Bits des Registers besitzen dabei eine Wertigkeit, die sich aus der Stelle des Bits im Byte ergibt. Dies ist völlig analog zu dem uns vertrauten Dezimalsystem. Auch dort besitzt eine Ziffer in einer Zahl eine bestimmte Wertigkeit, je nach dem, an welcher Position diese Ziffer in der Zahl auftaucht. So hat z.B. die Ziffer 1 in der Zahl 12 die Wertigkeit &#039;Zehn&#039;, während sie in der Zahl 134 die Wertigkeit &#039;Hundert&#039; besitzt. Und so wie im Dezimalsystem die Wertigkeit einer Stelle immer das Zehnfache der Wertigkeit der Stelle unmittelbar rechts von ihr ist, so ist im Binärsystem die Wertigkeit einer Stelle immer das 2-fache der Stelle rechts von ihr.&lt;br /&gt;
&lt;br /&gt;
Die Zahl 4632 im Dezimalsystem kann also so aufgefasst werden:&lt;br /&gt;
&lt;br /&gt;
   4632  =     4 * 1000          ( 1000 = 10 hoch 3 )&lt;br /&gt;
            +  6 * 100           (  100 = 10 hoch 2 )&lt;br /&gt;
            +  3 * 10            (   10 = 10 hoch 1 )&lt;br /&gt;
            +  2 * 1             (    1 = 10 hoch 0 )&lt;br /&gt;
&lt;br /&gt;
Völlig analog ergibt sich daher folgendes für z.B. die 8 Bit Binärzahl &amp;lt;b&amp;gt;0b10011011&amp;lt;/b&amp;gt; (um Binärzahlen von Dezimalzahlen zu unterscheiden, wird ein &amp;lt;b&amp;gt;0b&amp;lt;/b&amp;gt; vorangestellt):&lt;br /&gt;
&lt;br /&gt;
  0b10011010     =    1 * 128    ( 128 = 2 hoch 7 )&lt;br /&gt;
                   +  0 * 64     (  64 = 2 hoch 6 )&lt;br /&gt;
                   +  0 * 32     (  32 = 2 hoch 5 )&lt;br /&gt;
                   +  1 * 16     (  16 = 2 hoch 4 )&lt;br /&gt;
                   +  1 * 8      (   8 = 2 hoch 3 )&lt;br /&gt;
                   +  0 * 4      (   4 = 2 hoch 2 )&lt;br /&gt;
                   +  1 * 2      (   2 = 2 hoch 1 )&lt;br /&gt;
                   +  1 * 1      (   1 = 2 hoch 0 )&lt;br /&gt;
&lt;br /&gt;
Ausgerechnet (um die entsprechende Dezimalzahl zu erhalten) ergibt das&lt;br /&gt;
128 + 16 + 8 + 2 + 1 = 155. Die Binärzahl &amp;lt;b&amp;gt;0b10011011&amp;lt;/b&amp;gt; entspricht also der Dezimalzahl &amp;lt;b&amp;gt;155&amp;lt;/b&amp;gt;. Es ist wichtig, sich klar zu machen, dass es zwischen Binär- und Dezimalzahlen keinen grundsätzlichen Unterschied gibt. Beides sind nur verschiedene Schreibweisen für das Gleiche: Eine Zahl. Während wir Menschen an das Dezimalsystem gewöhnt sind, ist das Binärsystem für einen Computer geeigneter, da es nur aus den 2 Ziffern 0 und 1 besteht, welche sich leicht in einem Computer darstellen lassen (Spannung, keine Spannung).&lt;br /&gt;
&lt;br /&gt;
Welches ist nun die größte Zahl, die mit 8 Bit dargestellt werden kann? Dabei handelt es sich offensichtlich um die Zahl &amp;lt;b&amp;gt;0b11111111&amp;lt;/b&amp;gt;. In Dezimalschreibweise wäre das die Zahl&lt;br /&gt;
&lt;br /&gt;
    0b11111111   =   1  *  128&lt;br /&gt;
                   + 1  *  64&lt;br /&gt;
                   + 1  *  32&lt;br /&gt;
                   + 1  *  16&lt;br /&gt;
                   + 1  *  8&lt;br /&gt;
                   + 1  *  4&lt;br /&gt;
                   + 1  *  2&lt;br /&gt;
                   + 1  *  1&lt;br /&gt;
&lt;br /&gt;
oder ausgerechnet: &amp;lt;b&amp;gt;255&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird also mit 8 Bit Arithmetik betrieben, wobei alle 8 Bit als signifikante Ziffern benutzt werden (also kein Vorzeichenbit, dazu später mehr), so kann damit im Zahlenraum &amp;lt;b&amp;gt;0&amp;lt;/b&amp;gt; bis &amp;lt;b&amp;gt;255&amp;lt;/b&amp;gt; gerechnet werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Binär          Dezimal               Binär         Dezimal&lt;br /&gt;
&lt;br /&gt;
   0b00000000        0                 0b10000000       128&lt;br /&gt;
   0b00000001        1                 0b10000001       129&lt;br /&gt;
   0b00000010        2                 0b10000010       130&lt;br /&gt;
   0b00000011        3                 0b10000011       131&lt;br /&gt;
   0b00000100        4                 0b10000100       132&lt;br /&gt;
   0b00000101        5                 0b10000101       133&lt;br /&gt;
     ...                                      ...&lt;br /&gt;
   0b01111100      124                 0b11111100       252&lt;br /&gt;
   0b01111101      125                 0b11111101       253&lt;br /&gt;
   0b01111110      126                 0b11111110       254&lt;br /&gt;
   0b01111111      127                 0b11111111       255&lt;br /&gt;
&lt;br /&gt;
= 8-Bit Arithmetik mit Berücksichtigung eines Vorzeichens =&lt;br /&gt;
&lt;br /&gt;
Soll mit Vorzeichen (also positiven und negativen Zahlen) gerechnet werden, so erhebt sich die Frage: Wie werden eigentlich positive bzw. negative Zahlen dargestellt? Alles was wir haben sind ja 8 Bit in einem Byte.&lt;br /&gt;
&lt;br /&gt;
== Problem der Kodierung des Vorzeichens ==&lt;br /&gt;
&lt;br /&gt;
Die Lösung des Problems besteht darin, dass ein Bit zur Anzeige des Vorzeichens benutzt wird. Im Regelfall wird dazu das am weitesten links stehende Bit benutzt. Von den verschiedenen Möglichkeiten, die sich hiermit bieten, wird in der Praxis fast ausschließlich mit dem sog. 2-er Komplement gearbeitet, da es Vorteile bei der Addition bzw. Subtraktion von Zahlen bringt. In diesem Fall muß nämlich das Vorzeichen einer Zahl überhaupt nicht berücksichtigt werden. Durch die Art und Weise der Bildung von negativen Zahlen kommt am Ende das Ergebnis mit dem korrekten Vorzeichen heraus.&lt;br /&gt;
&lt;br /&gt;
== 2-er Komplement ==&lt;br /&gt;
&lt;br /&gt;
Das 2-er Komplement verwendet das höchstwertige Bit eines Byte, das sog. MSB (= &amp;lt;b&amp;gt;M&amp;lt;/b&amp;gt;ost &amp;lt;b&amp;gt;S&amp;lt;/b&amp;gt;ignificant &amp;lt;b&amp;gt;B&amp;lt;/b&amp;gt;it) zur Anzeige des Vorzeichens. Ist dieses Bit 0, so ist die Zahl positiv. Ist es 1, so handelt es sich um eine negative Zahl. Die 8-Bit Kombination &amp;lt;b&amp;gt;0b10010011&amp;lt;/b&amp;gt; stellt also eine negative Zahl dar, &amp;lt;b&amp;gt;wenn und nur wenn diese Bitkombination überhaupt als vorzeichenbehaftete Zahl aufgefasst werden soll&amp;lt;/b&amp;gt;. Anhand der Bitkombination alleine ist es also nicht möglich, eine definitive Aussage zu treffen, ob es sich um eine vorzeichenbehaftete Zahl handelt oder nicht. Erst wenn durch den Zusammenhang klar ist, dass man es mit vorzeichenbehafteten Zahlen zu tun hat, bekommt das MSB die Sonderbedeutung des Vorzeichens.&lt;br /&gt;
&lt;br /&gt;
Um bei einer Zahl das Vorzeichen zu wechseln, geht man wie folgt vor:&lt;br /&gt;
* Zunächst wird das 1-er Komplement gebildet, indem alle Bits umgedreht werden. Aus 0 wird 1 und aus 1 wird 0&lt;br /&gt;
* Danach wird aus diesem Zwischenergebnis das 2-er Komplement gebildet, indem noch 1 addiert wird.&lt;br /&gt;
&lt;br /&gt;
Diese Vorschrift kann immer dann benutzt werden, wenn das Vorzeichen einer Zahl gewechselt werden soll. Er macht aus positiven Zahlen negative und aus negativen Zahlen positive.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039; Es soll die Binärdarstellung für -92 gebildet werden. Dazu benötigt man zunächst die Binärdarstellung für +92, welche &amp;lt;b&amp;gt;0b01011100&amp;lt;/b&amp;gt; lautet. Diese wird jetzt nach der Vorschrift für 2-er Komplemente negiert und damit negativ gemacht.&lt;br /&gt;
&lt;br /&gt;
   0b01011100            Ausgangszahl&lt;br /&gt;
   0b10100011            1-er Komplement, alle Bits umdrehen&lt;br /&gt;
   0b10100100            noch 1 addieren&lt;br /&gt;
&lt;br /&gt;
Die Binärdarstellung für -92 lautet also &amp;lt;b&amp;gt;0b10100100&amp;lt;/b&amp;gt;. Das gesetzte MSB weist diese Binärzahl auch tatsächlich als negative Zahl aus.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039; Gegeben sei die Binärzahl &amp;lt;b&amp;gt;0b00111000&amp;lt;/b&amp;gt;, welche als vorzeichenbehaftete Zahl anzusehen ist. Welcher Dezimalzahl entspricht diese Binärzahl?&lt;br /&gt;
&lt;br /&gt;
Da das MSB nicht gesetzt ist, handelt es sich um eine positive Zahl und die Umrechnung kann wie im Fall der vorzeichenlosen 8-Bit Zahlen erfolgen. Das Ergebnis lautet also +56 ( = 0 * 128 + 0 * 64 + 1 * 32 + 1 * 16 + 1 * 8 + 0 * 4 + 0 * 2 + 0 * 1 )&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039; Gegeben sei die Binärzahl &amp;lt;b&amp;gt;0b10011001&amp;lt;/b&amp;gt;, welche als vorzeichenbehaftete Zahl anzusehen ist. Welcher Dezimalzahl entspricht diese Binärzahl?&lt;br /&gt;
&lt;br /&gt;
Da das MSB gesetzt ist, handelt es sich um eine negative Zahl. Daher wird diese Zahl zunächst negiert um dadurch eine positive Zahl zu erhalten.&lt;br /&gt;
&lt;br /&gt;
    0b10011001       Originalzahl&lt;br /&gt;
    0b01100110       1-er Komplement, alle Bits umdrehen&lt;br /&gt;
    0b01100111       2-er Komplement, noch 1 addiert&lt;br /&gt;
&lt;br /&gt;
Die zu 0b10011001 gehörende positive Binärzahl lautet also 0b01100111. Da es sich um eine positive Zahl handelt, kann sie wiederum ganz normal, wie vorzeichenlose Zahlen, in eine Dezimalzahl umgerechnet werden. Das Ergebnis lautet 103 ( = 0 * 128 + 1 * 64 + 1 * 32 + 0 * 16 + 0 * 8 + 1 * 4 + 1 * 2 + 1 * 1). Da aber von einer negativen Zahl ausgegangen wurde, ist &amp;lt;b&amp;gt;0b10011001&amp;lt;/b&amp;gt; die binäre Darstellung der Dezimalzahl -103.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039; Gegeben sei dieselbe Binärzahl &amp;lt;b&amp;gt;0b10011001&amp;lt;/b&amp;gt;. Aber diesmal sei sie als vorzeichenlose Zahl aufzufassen. Welcher Dezimalzahl entspricht diese Binärzahl?&lt;br /&gt;
&lt;br /&gt;
Da die Binärzahl als vorzeichenlose Zahl aufzufassen ist, hat das MSB keine spezielle Bedeutung. Die Umrechnung erfolgt also ganz normal: 0b10011001 = 1 * 128 + 0 * 64 + 0 * 32 + 1 * 16 + 1 * 8 + 0 * 4 + 0 * 2 + 1 * 1 = 153.&lt;br /&gt;
&lt;br /&gt;
= spezielle Statusflags =&lt;br /&gt;
&lt;br /&gt;
Im Statusregister des Prozessors gibt es eine Reihe von Flags, die durch Rechenergebnisse beeinflusst werden, bzw. in Berechnungen einfließen können.&lt;br /&gt;
&lt;br /&gt;
== Carry ==&lt;br /&gt;
Das Carry-Flag zeigt an ob bei einer Berechnung ein Über- oder Unterlauf erfolgt ist.&lt;br /&gt;
&lt;br /&gt;
Wie das?&lt;br /&gt;
&lt;br /&gt;
Angenommen es müssen 2 8-Bit Zahlen addiert werden.&lt;br /&gt;
&lt;br /&gt;
    10100011&lt;br /&gt;
  + 11110011&lt;br /&gt;
   ---------&lt;br /&gt;
   100010110&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Addition umfasst 9 Bit. Da aber die Register nur 8 Bit fassen können, würde das bedeuten, dass das MSB (das 9. Bit) verlorengeht. In diesem Fall sagt man: Die Addition ist übergelaufen. Damit man darauf reagieren kann, wird dieses 9. Bit allerdings in das Carry Flag übertragen und es gibt auch spezielle Befehle, die dieses Carry Flag in ihren Berechnungen berücksichtigen können.&lt;br /&gt;
&lt;br /&gt;
= Inkrementieren / Dekrementieren =&lt;br /&gt;
&lt;br /&gt;
Erstaunlich viele Operationen in einem Computer-Programm entfallen auf die Operationen &#039;Zu einer Zahl 1 addieren&#039; bzw. &#039;Von einer Zahl 1 subtrahieren&#039;. Dementsprechend enthalten die meisten Mikroprozessoren die Operationen &amp;lt;b&amp;gt;Inkrementieren&amp;lt;/b&amp;gt; (um 1 erhöhen) bzw. &amp;lt;b&amp;gt;Dekrementieren&amp;lt;/b&amp;gt; (um 1 verringern) als eigenständigen Assemblerbefehl. So auch der &amp;lt;b&amp;gt;ATMega8&amp;lt;/b&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==AVR Befehle==&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        inc  r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        dec  r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Operation ist einfach zu verstehen. Das jeweils angegebene Register (hier wieder am Register r16 gezeigt) wird um 1 erhöht bzw. um 1 verringert. Dabei wird die Zahl im Register als vorzeichenlose Zahl angesehen. Enthält das Register bereits die größtmögliche Zahl (0b11111111 oder dezimal 255), so erzeugt ein weiteres Inkrementieren die kleinstmögliche Zahl (0b00000000 oder dezimal 0) bzw. umgekehrt dekrementiert 0 zu 255.&lt;br /&gt;
&lt;br /&gt;
= Addition =&lt;br /&gt;
Auf einem Mega8 gibt es nur eine Möglichkeit, um eine Addition durchzuführen: Die beiden zu addierenden Zahl müssen in zwei Registern stehen.&lt;br /&gt;
&lt;br /&gt;
== AVR Befehle ==&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
     add  r16, r17      ; Addition der Register r16 und r17. Das Ergebnis wird&lt;br /&gt;
                        ; im Register r17 abgelegt&lt;br /&gt;
     adc  r16, r17      ; Addition der Register r16 und r17, wobei das Carry-Bit&lt;br /&gt;
                        ; noch zusätzlich mit addiert wird.&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Addition zweier Register wird ein möglicher Überlauf in allen Fällen im Carry Bit abgelegt. Daraus erklärt sich dann auch das Vorhandensein eines Additionsbefehls, der das Carry-Bit noch zusätzlich mitaddiert: Man benötigt ihn zum Aufbau einer Addition die mehr als 8 Bit umfasst. Die niederwertigsten Bytes werden mit einem &amp;lt;b&amp;gt;add&amp;lt;/b&amp;gt; addiert und alle weiteren höherwertigen Bytes werden, vom Niederwertigsten zum Höchstwertigsten, mittels &amp;lt;b&amp;gt;adc&amp;lt;/b&amp;gt; addiert. Dadurch werden eventuelle Überträge automatisch berücksichtigt.&lt;br /&gt;
&lt;br /&gt;
= Subtraktion =&lt;br /&gt;
Subtraktionen können auf einem AVR in zwei unterschiedlichen Arten ausgeführt werden. Entweder es werden zwei Register voneinander subtrahiert oder es wird von einem Register eine konstante Zahl abgezogen. Beide Varianten gibt es wiederrum in den Ausführungen mit und ohne Berücksichtigung des Carry Flags&lt;br /&gt;
&lt;br /&gt;
==AVR Befehle==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
     sub  r16, r17      ; Subtraktion des Registers r17 von r16. Das Ergebnis wird&lt;br /&gt;
                        ; im Register r16 abgelegt&lt;br /&gt;
     sbc  r16, r17      ; Subtraktion des Registers r17 von r16, wobei das Carry-Bit&lt;br /&gt;
                        ; noch zusätzlich mit subtrahiert wird. Das Ergebnis wird&lt;br /&gt;
                        ; im Register r16 abgelegt&lt;br /&gt;
     subi r16, zahl     ; Die Zahl (als Konstante) wird vom Register r16 subtrahiert.&lt;br /&gt;
                        ; Das Ergebnis wird im Register r16 abgelegt&lt;br /&gt;
     sbci r16, zahl     ; Subtraktion einer konstanten Zahl vom Register r16, wobei&lt;br /&gt;
                        ; zusätzlich noch das Carry-Bit mit subtrahiert wird.&lt;br /&gt;
                        ; Das Ergebnis wird im Register r16 abgelegt.&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Multiplikation =&lt;br /&gt;
Multiplikation kann auf einem AVR je nach konkretem Typ auf zwei unterschiedliche Arten ausgeführt werden. Während die größeren ATMega Prozessoren über einen Hardwaremultiplizierer verfügen, ist dieser bei den kleineren Tiny Prozessoren nicht vorhanden. Hier muß die Multiplikation quasi zu Fuß durch entsprechende Addition von Teilresultaten erfolgen.&lt;br /&gt;
&lt;br /&gt;
== Hardwaremultiplikation ==&lt;br /&gt;
Vorzeichenbehaftete und vorzeichenlose Zahlen werden unterschiedlich multipliziert. Denn im Falle eines Vorzeichens darf ein gesetztes 7. Bit natürlich nicht in die eigentliche Berechnung mit einbezogen werden. Statt dessen steuert dieses Bit (eigentlich die beiden MSB der beiden beteiligten Zahlen) das Vorzeichen des Ergebnisses. Die Hardwaremultiplikation ist auch dahingehend eingeschränkt, dass das Ergebnis einer Multiplikation immer in den Registerpärchen &#039;&#039;r0&#039;&#039; und &#039;&#039;r1&#039;&#039; zu finden ist. Dabei steht das LowByte (also die unteren 8 Bit) des Ergebnisses in &#039;&#039;r0&#039;&#039; und das HighByte in &#039;&#039;r1&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== AVR Befehl ===&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    mul   r16, r17      ; multipliziert r16 mit r17. Beide Registerinhalte werden&lt;br /&gt;
                        ; als vorzeichenlose Zahlen aufgefasst.&lt;br /&gt;
                        ; Das Ergebnis der Multiplikation ist in den Registern r0 und r1&lt;br /&gt;
                        ; zu finden.&lt;br /&gt;
&lt;br /&gt;
    muls  r16, r17      ; multipliziert r16 mit r17. Beide Registerinhalte werden&lt;br /&gt;
                        ; als vorzeichenbehaftete Zahlen aufgefasst.&lt;br /&gt;
                        ; Das Ergebnis der Multiplikation ist in den Registern r0 und r1&lt;br /&gt;
                        ; zu finden und stellt ebenfalls eine vorzeichenbehaftete&lt;br /&gt;
                        ; Zahl dar.&lt;br /&gt;
&lt;br /&gt;
    mulsu r16, r17      ; multipliziert r16 mit r17, wobei r16 als vorzeichenbehaftete&lt;br /&gt;
                        ; Zahl aufgefasst wird und r17 als vorzeichenlose Zahl.&lt;br /&gt;
                        ; Das Ergebnis der Multiplikation ist in den Registern r0 und r1&lt;br /&gt;
                        ; zu finden und stellt eine vorzeichenbehaftete Zahl dar.&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Multiplikation in Software ==&lt;br /&gt;
Multiplikation in Software ist nicht weiter schwierig. Man erinnere sich daran, wie Multiplikationen in der Grundschule gelehrt wurden:&lt;br /&gt;
Zunächst stand da das kleine Einmal-Eins, welches auswendig gelernt wurde. Mit diesen Kenntnissen konnten dann auch größere Multiplikationen angegangen werden, indem der Multiplikand mit jeweils einer Stelle des Multiplikators multipliziert wurde und die Zwischenergebnisse, geeignet verschoben, addiert wurden. Die Verschiebung um eine Stelle entspricht dabei einer Multiplikation mit 10.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039; Zu multiplizieren sei 3456 * 7812&lt;br /&gt;
&lt;br /&gt;
       3456     * 7812&lt;br /&gt;
       ---------------&lt;br /&gt;
      24192     &amp;lt;-+|||&lt;br /&gt;
  +    27648    &amp;lt;--+||&lt;br /&gt;
  +      3456   &amp;lt;---+|&lt;br /&gt;
  +       6912  &amp;lt;----+&lt;br /&gt;
      --------&lt;br /&gt;
      26998272&lt;br /&gt;
&lt;br /&gt;
Im Binärsystem funktioniert Multiplikation völlig analog. Nur ist hier das kleine Einmaleins sehr viel einfacher! Es gibt nur 4 Multiplikationen (anstatt 100 im Dezimalsystem):&lt;br /&gt;
&lt;br /&gt;
    0 * 0   = 0&lt;br /&gt;
    0 * 1   = 0&lt;br /&gt;
    1 * 0   = 0&lt;br /&gt;
    1 * 1   = 1&lt;br /&gt;
&lt;br /&gt;
Es gibt lediglich einen kleinen Unterschied gegenüber dem Dezimalsystem: Anstatt zunächst alle Zwischenergebnisse aufzulisten und erst danach die Summe zu bestimmen, werden wir ein neues Zwischenergebnis gleich in die Summe einrechnen. Dies deshalb, da Additionen von mehreren Zahlen im Binärsystem im Kopf sehr leicht zu Flüchtigkeitsfehlern führen (durch die vielen 0-en und 1-en). Weiters wird eine einfache Tatsache benutzt: 1 mal eine Zahl ergibt wieder die Zahl, während 0 mal eine Zahl immer 0 ergibt. Dadurch braucht man im Grunde bei einer Multiplikation überhaupt nicht zu multiplizieren, sondern eigentlich nur die Entscheidung treffen: Muss die Zahl geeignet verschoben addiert werden oder nicht?&lt;br /&gt;
&lt;br /&gt;
    0b00100011         * 0b10001001&lt;br /&gt;
    --------------------------------&lt;br /&gt;
      00100011          &amp;lt;--+|||||||&lt;br /&gt;
 +     00000000         &amp;lt;---+||||||&lt;br /&gt;
      ---------              ||||||&lt;br /&gt;
      001000110              ||||||&lt;br /&gt;
 +      00000000        &amp;lt;----+|||||&lt;br /&gt;
      ----------              |||||&lt;br /&gt;
      0010001100              |||||&lt;br /&gt;
 +       00000000       &amp;lt;-----+||||&lt;br /&gt;
      -----------              ||||&lt;br /&gt;
      00100011000              ||||&lt;br /&gt;
 +        00100011      &amp;lt;------+|||&lt;br /&gt;
      ------------              |||&lt;br /&gt;
      001001010011              |||&lt;br /&gt;
 +         00000000     &amp;lt;-------+||&lt;br /&gt;
      -------------              ||&lt;br /&gt;
      0010010100110              ||&lt;br /&gt;
 +          00000000    &amp;lt;--------+|&lt;br /&gt;
      --------------              |&lt;br /&gt;
      00100101001100              |&lt;br /&gt;
 +           00100011   &amp;lt;---------+&lt;br /&gt;
      ---------------&lt;br /&gt;
      001001010111011&lt;br /&gt;
&lt;br /&gt;
Man sieht auch, wie bei der Multiplikation zweier 8 Bit Zahlen sehr schnell ein 16 Bit Ergebnis entsteht. Dies ist auch der Grund, warum die Hardwaremultiplikation immer 2 Register zur Aufnahme des Ergebnisses benötigt.&lt;br /&gt;
&lt;br /&gt;
Ein Assembler Code, der diese Strategie im wesentlichen verwirklicht, sieht z.B. so aus. Dieser Code wurde nicht auf optimale Laufzeit getrimmt, sondern es soll im Wesentlichen eine 1:1 Umsetzung des oben gezeigten Schemas sein. Einige der verwendeten Befehle wurden im Rahmen dieses Tutorials an dieser Stelle noch nicht besprochen. Speziell die Schiebe- (&amp;lt;b&amp;gt;lsl&amp;lt;/b&amp;gt;) und Rotier- (&amp;lt;b&amp;gt;rol&amp;lt;/b&amp;gt;) Befehle sollten in der AVR Befehlsübersicht genau studiert werden, um ihr Zusammenspiel mit dem Carry Flag zu verstehen. Nur soviel als Hinweis: Das Carry Flag dient in der &amp;lt;b&amp;gt;lsl&amp;lt;/b&amp;gt; / &amp;lt;b&amp;gt;rol&amp;lt;/b&amp;gt; Sequenz als eine Art Zwischenspeicher, um das höherwertigste Bit aus dem Register r0 beim Verschieben in das Register r1 verschieben zu können. Der &amp;lt;b&amp;gt;lsl&amp;lt;/b&amp;gt; verschiebt alle Bits des Registers um 1 Stelle nach links, wobei das vorhergehende MSB ins Carry Bit wandert und rechts ein 0-Bit nachrückt. Der &amp;lt;b&amp;gt;rol&amp;lt;/b&amp;gt; verschiebt ebenfalls alle Stellen eines Registers um 1 Stelle nach links. Diesmal wird aber rechts nicht mit einem 0-Bit aufgefüllt, sondern an dieser Stelle wird der momentane Inhalt des Carry Bits eingesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi  r16, 0b00100011  ; Multiplikator&lt;br /&gt;
    ldi  r17, 0b10001001  ; Multiplikand&lt;br /&gt;
                          ; Berechne r16 * r17&lt;br /&gt;
&lt;br /&gt;
    ldi  r18, 8          ; 8 mal verschieben und gegebenenfalls addieren&lt;br /&gt;
    clr  r19             ; 0 wird für die 16 Bit Addition benötigt&lt;br /&gt;
    clr  r0              ; Ergebnis Low Byte auf 0 setzen&lt;br /&gt;
    clr  r1              ; Ergebnis High Byte auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
mult:&lt;br /&gt;
    lsl  r0              ; r1:r0 einmal nach links verschieben&lt;br /&gt;
    rol  r1&lt;br /&gt;
    lsl  r17             ; Das MSB von r17 ins Carry schieben&lt;br /&gt;
    brcc noadd           ; Ist dieses MSB (jetzt im Carry) eine 1?&lt;br /&gt;
    add  r0,r16          ; Wenn ja, dann r16 zum Ergebnis addieren&lt;br /&gt;
    adc  r1,r19&lt;br /&gt;
&lt;br /&gt;
noadd:&lt;br /&gt;
    dec  r18             ; Wurden alle 8 Bit von r17 abgearbeitet?&lt;br /&gt;
    brne mult            ; Wenn nicht, dann ein erneuter Verschiebe/Addier Zyklus&lt;br /&gt;
&lt;br /&gt;
                         ; r0 enthält an dieser Stelle den Wert 0b10111011&lt;br /&gt;
                         ; r1 enthält 0b00010010&lt;br /&gt;
                         ; Gemeinsam bilden r1 und r0 also die Zahl&lt;br /&gt;
                         ; 0b0001001010111011&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Division =&lt;br /&gt;
Anders als bei der Multiplikation, gibt es auch auf einem ATMega-Prozessor keine hardwaremässige Divisionseinheit. Divisionen müssen also in jedem Fall mit einer speziellen Routine, die im wesentlichen auf Subtraktionen beruht, erledigt werden.&lt;br /&gt;
== Division in Software ==&lt;br /&gt;
&lt;br /&gt;
Um die Vorgangsweise bei der binären Division zu verstehen, wollen wir wieder zunächst anhand der gewohnten dezimalen Division untersuchen wie sowas abläuft.&lt;br /&gt;
&lt;br /&gt;
Angenommen es soll dividiert werden: 92 / 5 ( 92 ist der Dividend, 5 ist der Divisor)&lt;br /&gt;
&lt;br /&gt;
Wie haben Sie es in der Grundschule gelernt? Wahrscheinlich so wie der Autor auch:&lt;br /&gt;
&lt;br /&gt;
    938 : 4 = 234&lt;br /&gt;
   ---&lt;br /&gt;
   -8&lt;br /&gt;
   ----&lt;br /&gt;
    1&lt;br /&gt;
    13&lt;br /&gt;
   -12&lt;br /&gt;
    ---&lt;br /&gt;
     1&lt;br /&gt;
     18&lt;br /&gt;
    -16&lt;br /&gt;
     --&lt;br /&gt;
      2 Rest&lt;br /&gt;
&lt;br /&gt;
Der Vorgang war: Man nimmt die erste Stelle des Dividenden (9) und ruft seine gespeicherte Einmaleins Tabelle ab, um festzustellen, wie oft der Divisor in dieser Stelle enthalten ist. In diesem konkreten Fall ist die erste Stelle 9 und der Divisor 4. 4 ist in 9 zweimal enthalten. Also ist 2 die erste Ziffer des Ergebnisses. 2 mal 4 ergibt aber 8 und diese 8 werden von den 9 abgezogen, übrig bleibt 1.&lt;br /&gt;
Aus dem Dividenden wird die nächste Ziffer (3) heruntergezogen und man erhält mit der 1 aus dem vorhergehenden Schritt 13.&lt;br /&gt;
Wieder dasselbe Spiel: Wie oft ist 4 in 13 enthalten? 3 mal (3 ist die nächste Ziffer des Ergebnisses) und 3 * 4 ergibt 12. Diese 12 von den 13 abgezogen macht 1. Zu dieser 1 gesellt sich wieder die nächste Ziffer des Dividenden, 8, um so 18 zu bilden.&lt;br /&gt;
Wie oft ist 4 in 18 enthalten? 4 mal (4 ist die nächste Ziffer des Ergebnisses), denn 4 mal 4 macht 16, und das von den 18 abgezogen ergibt 2.&lt;br /&gt;
Da es keine nächste Ziffer im Dividenden mehr gibt, lautet also das Resultat:&lt;br /&gt;
938 : 4 ergibt 234 und es bleiben 2 Rest.&lt;br /&gt;
&lt;br /&gt;
Die binäre Division funktioniert dazu völlig analog. Es gibt nur einen kleinen Unterschied, der einem sogar das Leben leichter macht. Es geht um den Schritt: Wie oft ist x in y enthalten?&lt;br /&gt;
Dieser Schritt ist in der binären Division besonders einfach, da das Ergebnis dieser Fragestellung nur 0 oder 1 sein kann. Das bedeutet aber auch: Entweder ist der Divisior in der zu untersuchenden Zahl enthalten, oder er ist es nicht. Das kann aber ganz leicht entschieden werden: Ist die Zahl größer oder gleich dem Divisior, dann ist der Divisor enthalten und zum Ergebnis kann eine 1 hinzugefügt werden. Ist die Zahl kleiner als der Divisior, dann ist der Divisior nicht enthalten und die nächste Ziffer des Ergebnisses ist eine 0.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Es soll die Division 0b01101100 : 0b00001001 ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Es wird wieder mit der ersten Stelle begonnen und die oben ausgeführte Vorschrift angewandt.&lt;br /&gt;
&lt;br /&gt;
   0b01101101 : 0b00001001 = 0b00001100&lt;br /&gt;
                               ^^^^^^^^&lt;br /&gt;
                               ||||||||&lt;br /&gt;
     0                ---------+|||||||   1001 ist in 0 0-mal enthalten&lt;br /&gt;
    -0                          |||||||&lt;br /&gt;
    --                          |||||||&lt;br /&gt;
     0                          |||||||&lt;br /&gt;
     01               ----------+||||||   1001 ist in 1 0-mal enthalten&lt;br /&gt;
    - 0                          ||||||&lt;br /&gt;
     --                          ||||||&lt;br /&gt;
     01                          ||||||&lt;br /&gt;
     011              -----------+|||||   1001 ist in 11 0-mal enthalten&lt;br /&gt;
    -  0                          |||||&lt;br /&gt;
     ---                          |||||&lt;br /&gt;
     011                          |||||&lt;br /&gt;
     0110             ------------+||||   1001 ist in 110 0-mal enthalten&lt;br /&gt;
    -   0                          ||||&lt;br /&gt;
     ----                          ||||&lt;br /&gt;
     0110                          ||||&lt;br /&gt;
     01101            -------------+|||   1001 ist in 1101 1-mal enthalten&lt;br /&gt;
    - 1001                          |||&lt;br /&gt;
     -----                          |||&lt;br /&gt;
      0100                          |||&lt;br /&gt;
      01001           --------------+||   1001 ist in 1001 1-mal enthalten&lt;br /&gt;
     - 1001                          ||&lt;br /&gt;
      -----                          ||&lt;br /&gt;
      00000                          ||&lt;br /&gt;
      000000          ---------------+|   1001 ist in 0 0-mal enthalten&lt;br /&gt;
    -      0                          |&lt;br /&gt;
      ------                          |&lt;br /&gt;
      0000001         ----------------+   1001 ist in 1 0-mal enthalten&lt;br /&gt;
     -      0&lt;br /&gt;
      -------&lt;br /&gt;
            1 Rest&lt;br /&gt;
&lt;br /&gt;
Die Division liefert also das Ergebnis 0b00001100, wobei ein Rest von 1 bleibt. Der Dividend 0b01101101 entspricht der Dezimalzahl 109, der Divisor 0b00001001 der Dezimalzahl 9. Und wie man sich mit einem Taschenrechner leicht überzeugen kann, ergibt die Division von 109 durch 9 einen Wert von 12, wobei 1 Rest bleibt. Die Binärzahl für 12 lautet 0b00001100, das Ergebnis stimmt also.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi  r16, 109   ; Dividend&lt;br /&gt;
    ldi  r17,   9   ; Divisor&lt;br /&gt;
&lt;br /&gt;
                    ; Division r16 : r17&lt;br /&gt;
&lt;br /&gt;
    ldi  r18,   8   ; 8 Bit Division&lt;br /&gt;
    clr  r19        ; Register für die Zwischenergebnisse / Rest&lt;br /&gt;
    clr  r20        ; Ergebnis&lt;br /&gt;
&lt;br /&gt;
divloop:&lt;br /&gt;
    lsl  r16        ; Zwischenergebnis mal 2 nehmen und das&lt;br /&gt;
    rol  r19        ; nächste Bit des Dividenden anhängen&lt;br /&gt;
&lt;br /&gt;
    lsl  r20        ; das Ergebnis auf jeden Fall mal 2 nehmen,&lt;br /&gt;
                    ; das hängt effektiv eine 0 an das Ergebnis an.&lt;br /&gt;
                    ; Sollte das nächste Ergebnis-Bit 1 sein, dann wird&lt;br /&gt;
                    ; diese 0 in Folge durch eine 1 ausgetauscht&lt;br /&gt;
&lt;br /&gt;
    cp   r19, r17   ; ist der Divisor größer?&lt;br /&gt;
    brlo div_zero   ; wenn nein, dann bleibt die 0&lt;br /&gt;
    sbr  r20, 1     ; wenn ja, dann jetzt die 0 durch eine 1 austauschen ...&lt;br /&gt;
    sub  r19, r17   ; ... und den Divisor abziehen&lt;br /&gt;
&lt;br /&gt;
div_zero:&lt;br /&gt;
    dec  r18        ; das Ganze 8 mal wiederholen&lt;br /&gt;
    brne divloop&lt;br /&gt;
&lt;br /&gt;
                    ; in r20 steht das Ergebnis der Division&lt;br /&gt;
                    ; in r19 steht der bei der Division entstehende Rest&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Arithmetik mit mehr als 8 Bit =&lt;br /&gt;
&lt;br /&gt;
Eine Sammlung von Algorithmen zur Arithmetik mit mehr als 8 Bit findet sich [[AVR_Arithmetik | hier]]. Die Grundprinzipien sind im wesentlichen identisch zu den in diesem Teil detailliert ausgeführten Prinzipien.&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Logik|&lt;br /&gt;
zurücklink=AVR-Tutorial: Logik|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Stack|&lt;br /&gt;
vorlink=AVR-Tutorial: Stack}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>195.233.250.6</name></author>
	</entry>
</feed>