<?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=Heha</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=Heha"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/Heha"/>
	<updated>2026-04-11T03:54:16Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Kondensator&amp;diff=107597</id>
		<title>Kondensator</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Kondensator&amp;diff=107597"/>
		<updated>2025-08-04T09:06:13Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* Flüssig-Elektrolyt */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein &#039;&#039;&#039;Kondensator&#039;&#039;&#039; ist ein passives elektrisches Bauteil mit zwei Anschlüssen, welches elektrische Energie in einem elektrischen Feld in Form von Ladung speichert. Die charakteristische Größe, die den Kondensator beschreibt, ist die Kapazität C, gemessen in Farad (F). Ein Kondensator besteht aus zwei Flächen gut leitfähigen Materials, die voneinander isoliert sind. Das Isolationsmaterial ist ein Dielektrikum und verleiht dem Kondensator je nach Material stark unterschiedliche Eigenschaften.&lt;br /&gt;
&lt;br /&gt;
== Aufbau eines Kondensators ==&lt;br /&gt;
Je größer die Fläche, je geringer der Abstand der Flächen und je höher die relative Permittivität \varepsilon_r - das Dielektrikum - desto höher ist die Kapazität des Bauteils. Der einfachste Kondensator besteht aus zwei voneinander isolierten Metallplatten, zwischen denen sich Luft als &amp;quot;Dielektrikum&amp;quot; befindet. Um die Fläche und damit die Kapazität zu vergrößern gibt es verschiedene Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
=== Wickelkondensator ===&lt;br /&gt;
Die einfachste Methode die Flächen zu vergrößern ist es, zwei leitfähige Folien mit einem Isolationsmaterial dazwischen aufzuwickeln. Diese Methode wird für so ziemlich alle Kondensatortypen verwendet. Manchmal wird der Wickel nach der Herstellung flachgepresst, um das Volumen zu optimieren.&lt;br /&gt;
&lt;br /&gt;
=== Schichtkondensator ===&lt;br /&gt;
Diese Aufbauart wird durch &amp;quot;stapeln&amp;quot; der unterschiedlichen Lagen erreicht. Verfahrenstechnisch ist dies oft wieder ein Wickel, jedoch meist auf bis zu 2 Meter durchmessenden Wickelrädern. Dieses ursprünglich von Siemens eingesetzte Verfahren wird auch aktuell noch verwendet, hauptsächlich für Folienkondensatoren bis in den mF-Bereich hinein. Nach dem Wickeln auf vieleckigen Rädern wird der Folienstapel geschnitten und auf die gewünschte Größe = Kapazität getrennt.&lt;br /&gt;
&lt;br /&gt;
=== Zusammenhang Baugröße, Spannung, Kapazität ===&lt;br /&gt;
Abgesehen vom Einfluss des Dielektrikums geht das Bauvolumen grundsätzlich linear zur Kapazität und quadratisch(!) zur Spannungsfestigkeit. Denn zur Verdopplung der Kapazität verdoppelt man die Kondensatorfläche, entsprechend einer Parallelschaltung von Kondensatoren. Zur Verdopplung der Spannungsfestigkeit wird wie bei einer Reihenschaltung die Dicke des Dielektrikums verdoppelt, was mit einer Halbierung der Kapazität einhergeht: Bei gleicher Kapazität muss daher auch die Kondensatorfläche verdoppelt werden.&lt;br /&gt;
&lt;br /&gt;
Somit ist das Bauvolumen direkt proportional zum maximalen Energiegehalt des Kondensators.&lt;br /&gt;
&lt;br /&gt;
== Kondensatortypen ==&lt;br /&gt;
(Vakuum-, Glimmer-, Gas- und Glaskondensatoren nicht berücksichtigt)&lt;br /&gt;
=== Keramik-Kondensator ===&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Keramikkondensator in [https://de.wikipedia.org/wiki/Keramikkondensator#Spannungsabh.C3.A4ngigkeit_der_Kapazit.C3.A4t Wikipedia]&#039;&#039;&#039;. Es gibt sie bedrahtet und in SMD. Bedrahtet gab es sie früher als Einschicht-Kapazität in Scheibenform, heutzutage nur noch als Vielschicht-Kondensator. Einschichtig nur für Spannungen im Kilovolt-Bereich.&lt;br /&gt;
&lt;br /&gt;
Kerkos sind sogenannte &amp;quot;Vielschicht-Kondensatoren&amp;quot; die aus mehreren hundert Lagen einer isolierenden Keramik (Titandioxid+Bariumtitanat), und einer elektrisch leitfähigen Metallisierung (Aluminium &amp;amp; Magnesiumsilikate) bestehen.&lt;br /&gt;
Dieser Typ ist wie der Folienkondensator für höchste Frequenzen geeignet, hat eine sehr geringe Baugröße, eine gute Temperaturstabilität aber piezoelektrische Eigenschaften. Die Kapazität pro mm³ variiert je nach Dielektrikum Z5U, Y5V, X7R, C0 (in abnehmender Kapazitätsdichte) und ist sowohl abhängig von Temperatur als auch der angelegten Spannung.&lt;br /&gt;
&lt;br /&gt;
Keramik-Kondensatoren sind sehr preisgünstig und werden überall dort eingesetzt, wo andere Kondensatortypen keinen Vorteil bringen. Zudem lösen sie mehr und mehr Elektrolytkondensatoren ab, die ihrerseits mit hoher Kapazität punkten: Die Lebensdauer, besonders bei hoher Temperatur, ist höher.&lt;br /&gt;
&lt;br /&gt;
=== Folien-Kondensator ===&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Folienkondensator in [https://de.wikipedia.org/wiki/Kunststoff-Folienkondensator Wikipedia]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Unterschiedliche Folienarten verleihen dem Kondensator stark unterschiedliche Eigenschaften wie z.B. temperatur- und alterungsbeständigkeit, Verlustfaktor, Isolationswiderstand, und die obere Grenzfrequenz, um nur die wichtigsten zu nennen. Die Dicke der Metallisierung hingegen entscheidet über den maximal zulässigen Rippelstrom / Impulsstromfestigkeit und darüber, ob sich ein Kondensator &amp;quot;selbst heilen&amp;quot; kann, oder nicht. Das Verfahren der &amp;quot;Selbstheilung&amp;quot; funktioniert nur bei Metallisierungen, da es darauf beruht, daß die im C gespeicherte Energie den Metallbelag beim Durchschlag durch die Isolation verdampfen kann. Ist die Energie zu gering, wird nur wenig Metall um den Durchschlag verdampft, und der Kurzschluß bleibt bestehen. Ist die Energie zu hoch, wird der Kondensator thermisch zerstört. Hat der C den richigen Energiegehalt, verdampft direkt um die Durchschlagstelle herum bis zu einigen mm der Metallisierung, und die Fehlstelle ist wieder isoliert. Im Übrigen ist eine Parallelsschaltung aus vielen,als selbstheilenden C&#039;s nicht unbedingt mehr &amp;quot;selbstheilend&amp;quot; da der Energieeintrag dann aus dem Gesamtverbund kommt, und oft zu hoch ist um nur eine kleine Fläche verdampfen zu lassen.&lt;br /&gt;
Impulsstrom-feste Kondensatoren zeichnen sich durch hohe Schichtstärken der Metallisierung aus, oder verwenden statt der Metallbeschichtung direkt eine Metallfolie. Hier wäre normalerweise ein &amp;quot;selbstheilen&amp;quot; gleichbedeutend mit einem so hohen Energieeintrag, daß das Bauteil zerstört wird.&lt;br /&gt;
&lt;br /&gt;
Hier eine kleine Übersicht mit der Bitte um Vervollständigung.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| style=&amp;quot;width:8em&amp;quot;| &#039;&#039;&#039;Folientyp&#039;&#039;&#039;  || Poly-&amp;lt;br&amp;gt;propylen || Poly-&amp;lt;br&amp;gt;styren || Poly-&amp;lt;br&amp;gt;carbonat  ||Poly-&amp;lt;br&amp;gt;ester|| Poly-&amp;lt;br&amp;gt;styrol  ||   Poly-&amp;lt;br&amp;gt;ethylen-&amp;lt;br&amp;gt;naphthalat ||   Polytetra-&amp;lt;br&amp;gt;fluor-&amp;lt;br&amp;gt;ethylen ||   Poly-&amp;lt;br&amp;gt;phenylen-&amp;lt;br&amp;gt;sulfid    ||   Metall-&amp;lt;br&amp;gt;Papier   &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Folien-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;markenname&#039;&#039;&#039; || Hostalen || -   || Makrofol, Makroflex ||  Mylar, Hostaphan ||   Styroflex ||  Kaladex||  Teflon|| Tedur, Ryton|| Papier&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Abkürzung&#039;&#039;&#039; || PP || -  || PC||  PET ||  PS||  PEN||  PTFE|| PPS|| -  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Typ&#039;&#039;&#039; || (F)KP, MKP || -KS, MKS   || KC, MKC ||  (F)KT, MKT  ||  -KS, MKS ||  (F)KN, MKN||  -|| (F)KI, MKI|| MP  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Temperatur-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /°C&#039;&#039;&#039; || ca 105 || -    || ca. 105 ||  125..150  ||  -  ||  150 ||  -|| 150 || 85  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Toleranz&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;+/-%&#039;&#039;&#039; || 1 || 1   || -||  1  ||  -  ||  -||  -|| -|| 20  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Grenzfrequenz /kHz&#039;&#039;&#039; || 100 || 400   || -||  -  ||  1000  ||  -||  -|| -|| -  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Selbstheilend&#039;&#039;&#039; || ja || -  || -||  ja  ||  -  ||  -||  -|| -|| ja  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Impuls-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;belastbarkeit&#039;&#039;&#039; || + || +   || ||  +  ||  -- ||  -||  -|| -|| +  &lt;br /&gt;
|}&lt;br /&gt;
(MK) = metallisierte Folie,  (F) = Metallbeläge z.B. Metallfolie (K) = Metallfolie+Kunststofffolie&lt;br /&gt;
&lt;br /&gt;
Für die Anwendung in Kondensatornetzteilen ist die Anwendung des Selbstheilungseffekts &#039;&#039;&#039;erforderlich&#039;&#039;&#039;. Daher findet man an dieser Stelle faktisch keine Keramikkondensatoren.&lt;br /&gt;
&lt;br /&gt;
=== Elektrolyt-Kondensator ===&lt;br /&gt;
Ein Elko unterscheidet sich hauptsächlich dadurch von anderen Kondensatoren, daß nur eine Elektrode aus einer Metallschicht besteht, die zweite aus einem festen, oder flüssigen Elektrolyten.&lt;br /&gt;
&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Elektrolytkondensator in [https://de.wikipedia.org/wiki/Elektrolytkondensator Wikipedia]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Fest-Elektrolyt ====&lt;br /&gt;
Diese Bauteile bestehen aus Polymeren, Metalloxiden, Braunstein, ... und sind wie die Elkos immer gepolt.&lt;br /&gt;
Als Metalloxide ist Tantal sehr, und Niob noch weniger verbreitet.&lt;br /&gt;
Takos sind &#039;&#039;&#039;SEHR&#039;&#039;&#039; empfindlich gegenüber schnellen Spannungsanstiegen bzw. Überspannung und sollten nie ohne Vorwiderstand direkt an einer niederohmigen Quelle (Batterie) geladen/betrieben werden. Takos verbrennen im Fehlerfall mit einer Stichflamme, daher empfiehlt es sich eine deutlich höhere Spannungsfestigkeit zu wählen und - bei direktem Anschluß an eine niederohmige Quelle - zusätzlich ein Lade-Vorwiderstand einzubauen. Treten zusätzlich hohe Temperaturen auf, ist die Spannungsfestigkeit mindestens zu verdoppeln.&lt;br /&gt;
Kondensatoren mit Niob statt Tantal sind weniger verbreitet, und noch sehr teuer. Ihr Vorteil ist z.B. die Unempfindlichkeit gegenüber schnellen Spannungsanstiegen, sie können direkt an jeder Quelle betrieben werden.&lt;br /&gt;
&lt;br /&gt;
==== Flüssig-Elektrolyt ====&lt;br /&gt;
Diese Bauteile bestehen aus zwei Lagen &#039;&#039;&#039;sehr stark angeätzter&#039;&#039;&#039; Aluminiumfolie - zur Vergrößerung der Oberfläche - und einem flüssigen (seltener &amp;quot;einem festen&amp;quot;) Elektrolyten. Die zweite Folie kontaktiert gleichzeitig den Elektrolyten. Der Elektrolyt ist häufig auf Alkoholen basierend, aus/mit Schwefelsäure oder Glykol.&lt;br /&gt;
Die Alufolie wird nach den ätzen anodisch oxidiert. Die Eloxal-Schicht ist mechanisch belastbar, und stellt die &#039;&#039;&#039;einzige&#039;&#039;&#039; Isolation zwischen den beiden Anschlüssen dar. Die Oxidation der Alufolie wird je nach Hersteller bei ca. 140..165% der Nennspannung durchgeführt. Die Oxidschicht baut sich durch den Elektrolyten bei langer Lagerung stark ab, daher sinkt die Spannungsfestigkeit eines Elkos mit seinem Alter. Alte Bauteile können - sofern noch Elektrolyt enthalten ist - durch spannungsrichtiges Anschließen an eine Spannungsquelle und langsamen erhöhen der Spannung bis über die Nennspannung hinaus wieder &amp;quot;formiert&amp;quot; werden. Achtung, hier besteht die Gefahr eines Kurzschlusses zwischen den Anschlüssen, daher hochohmigen Vorwiderstand verwenden!&lt;br /&gt;
Wird ein Elko verpolt betrieben, wird die Oxidschicht im Laufe der Zeit (Abhängig von Spannung und Temperatur) abgebaut, und der Kondensator schlägt durch.&lt;br /&gt;
&lt;br /&gt;
Dieser Typ von Kondensator ist Standard in Schaltnetzteilen, sowohl primärseitig als 400-V-Typ als auch sekundärseitig mit bspw. 6,3 V, je nach Ausgangsspannung. Wegen seines Bauvolumens herrscht Durchsteckmontage vor. Auch in Schaltungen der Audio- und Videotechnik, etwa Radios, CD-Spielern und Fernsehern, sowie auf PC-Mainboards für die Schaltregler für Prozessor-Kernspannungen, gibt es diese bei ansonsten durchgehender SMD-Bestückung fast immer nur als Durchsteckbauteil, scheint billiger zu sein. Ursache hierfür ist die zu geringe Hitzebeständigkeit von Elkos für den Reflow-Lötprozess; Elkos werden zuletzt mit einseitiger Hitze typischerweise mittels Schwall-Löten eingesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Doppelschicht-Kondensatoren ===&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Superkondensator in [https://de.wikipedia.org/wiki/Superkondensator Wikipedia]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Diese Bauteile werden unter Markennamen wie &amp;quot;Goldcap&amp;quot;, &amp;quot;Ultracap&amp;quot; oder &amp;quot;Supercap&amp;quot; vertrieben, besitzen meist ein flüssiges Elektrolyt haben aber nicht besonders viel mit den o.g. &amp;quot;Elektrolytkondensatoren&amp;quot; gemeinsam. Dieser Kondensator besitzt Eigenschaften eines Kondensators und einer Batterie. Der &amp;quot;Kondensator&amp;quot; ist hier aus (im Gegensatz zum Elko) zwei Lagen einer einseitig metallisierten Aktivkohle, einem Separator, und einem Elektrolyten. &lt;br /&gt;
Das verwendete Elektrolyt erfüllt unter definierten Betriebsbedingungen die Funktion eines zusätzlichen Dielektrikums, und trägt zusätzlich zur ohnehin schon sehr großen Fläche der Aktivkohle, durch seine extrem dünne Schichtdicke, zur sehr hohen Kapazität bei.&lt;br /&gt;
Im Gegensatz zu einer Batterie kann der Doppelschichtkondensator Energie sehr schnell aufnehmen und abgeben, und altert dabei nur im geringen Maße. 500000 Lade-/Entladezyklen sind erreichbar, wenn die Spannung pro Zelle nicht überschritten wird. Normal sind hier 2,3..2,5V.&lt;br /&gt;
&lt;br /&gt;
== Anwendungen ==&lt;br /&gt;
Hier wird für verschiedene Anwendungsfälle eine sinnvolle Lösung empfohlen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Anwendung&#039;&#039;&#039;  || &#039;&#039;&#039;Beispiel&#039;&#039;&#039; || &#039;&#039;&#039;Empfohlener Typ&#039;&#039;&#039;  ||&#039;&#039;&#039;Kommentar&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Stromversorgung || Netzteil|| Elko||&lt;br /&gt;
|- &lt;br /&gt;
| Stromversorgung || Zwischenkreis|| Elkos, Folienkondensatoren||&lt;br /&gt;
|-&lt;br /&gt;
| Datenerhalt || || Doppelschichtkondensator ||&lt;br /&gt;
|-&lt;br /&gt;
| Energiespeicher|| digitale Schaltungen|| Kerko || z.B. 47nF pro IC bzw. Vcc-Anschluß&lt;br /&gt;
|- &lt;br /&gt;
| Hohe Impulsenergie x00ms || Impulsschweißgerät, (capacitive discharge)|| Elko (Folie)||&lt;br /&gt;
|-&lt;br /&gt;
| Impulsfeste Kondensatoren || Coils shrinker, Coilgun, Railgun|| Folie||MKT10 oder noch besser FKT1&lt;br /&gt;
|-&lt;br /&gt;
| NF-Filter|| PWM als DAC || Tako/Elko/Kerko ||&lt;br /&gt;
|-&lt;br /&gt;
| HF-Filter|| Beispiel || Styroflex-Folie ||&lt;br /&gt;
|-&lt;br /&gt;
| Audio || Kopplung || Folie ||&lt;br /&gt;
|-&lt;br /&gt;
| Vorwiderstand || Motorkondensator || MP oder Folie ||&lt;br /&gt;
|-&lt;br /&gt;
| Vorwiderstand || Kondensatornetzteil|| Kerko/Folie || Achtung, nur X2-Typen!&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Mathematik zum Kondensator ==&lt;br /&gt;
&lt;br /&gt;
Die Größe eines Kondensators ist seine Kapazität (Formelzeichen &#039;&#039;C&#039;&#039;), die als Ladung (Formelzeichen &#039;&#039;Q&#039;&#039;) durch Spannung (Formelzeichen &#039;&#039;U&#039;&#039;) definiert ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{Q}{U}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Einheit für die Kapazität ist Farad:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; [C]=F=\frac{As}{V}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reihenschaltung ===&lt;br /&gt;
&lt;br /&gt;
Für eine Reihenschaltung von &#039;&#039;n&#039;&#039; Kondensatoren gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{C_{ges}}=\frac{1}{C_1}+\frac{1}{C_2}+\dots+\frac{1}{C_n}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_{ges}=U_1+U_2+\dots+U_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I_{ges}=I_1=I_2=\dots=I_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parallelschaltung ===&lt;br /&gt;
&lt;br /&gt;
Für eine Parallelschaltung von &#039;&#039;n&#039;&#039; Kondensatoren gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_{ges}=C_1+C_2+\dots+C_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_{ges}=U_1=U_2=\dots=U_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I_{ges}=I_1+I_2+\dots+I_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Gespeicherte Energie ===&lt;br /&gt;
&lt;br /&gt;
Die in einem Kondensator gespeicherte Energie lässt sich durch die Formel&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; W=\frac{C}{2} \cdot U^2 = \frac{1}{2 \cdot C} \cdot Q^2 = \frac{U}{2} \cdot Q &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
berechnen.&lt;br /&gt;
&lt;br /&gt;
== Praxis ==&lt;br /&gt;
&lt;br /&gt;
=== Polaritätszeichen ===&lt;br /&gt;
&lt;br /&gt;
Elektrolytkondensatoren (kurz Elkos) sind in der Regel gepolt, d. h. Gleichspannungspegel müssen in einer vorgeschriebenen Polarität angelegt werden, damit das Dielektrikum nicht zerstört wird.  Bei Aluminium-Elkos wird dabei generell der Minuspol gekennzeichnet, bei kleinen Bauformen mit einem Strich, bei größeren Bauformen befinden sich u. U. auch noch Minuszeichen in diesem Strich eingebettet. Bei Tantal-Elkos hingegen wird immer der Pluspol gekennzeichnet, sowohl bei SMD als auch THT-Bauformen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:SMD-Elkos.jpeg]]&lt;br /&gt;
&lt;br /&gt;
Die gezeigten Elkos haben folgende Werte (von links nach rechts):&lt;br /&gt;
&lt;br /&gt;
* 220 µF, Spannungsfestigkeit 6 V&lt;br /&gt;
* 100 µF (&amp;lt;math&amp;gt;10 \cdot 10^7&amp;lt;/math&amp;gt; pF), 16 V&lt;br /&gt;
* 22 µF, 10 V&lt;br /&gt;
* 1 µF, 35 V&lt;br /&gt;
* 2,2 µF (&amp;lt;math&amp;gt;22 \cdot 10^5&amp;lt;/math&amp;gt; pF), 10 V&lt;br /&gt;
&lt;br /&gt;
Die beim 100-µF-Kondensator zu findende Buchstabenschreibweise für die Spannungsfestigkeit ist wenig gebräuchlich, aber gelegentlich anzutreffen.  Die Zuordnung ist:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| style=&amp;quot;width:8em&amp;quot; | &#039;&#039;&#039;Buchstabe&#039;&#039;&#039; ||  e  ||   g   ||   j   ||   A   ||   C   ||   D   ||   E   ||   V   ||   H   ||   J   ||   K&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039; || 2.5 || 4   || 6,3 ||  10 ||  16 ||  20 ||  25 || 35 || 50 || 63 || 80&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Spannungsfestigkeit kann auch durch eine zweistellige Kombination aus einer Ziffer und einem Buchstaben codiert sein:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| style=&amp;quot;width:8em&amp;quot; | &#039;&#039;&#039;Kombination&#039;&#039;&#039;  ||   0G  ||   0L  ||  0J   ||  1A   ||  1C   ||  1E &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  || 4,0  || 5,5  || 6,3  ||  10  ||  16  ||  20 &lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Kombination&#039;&#039;&#039;                      ||   1H  ||   1J  ||  1K   ||  2A   ||  2Q   ||  2B &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  ||  50  ||  63  ||  80  || 100  || 110  || 125 &lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Kombination&#039;&#039;&#039;                      ||   2C  ||   2Z  ||  2D   ||  2P   ||  2E   ||  2F&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  || 160  || 180  || 200  || 220  || 250  || 315 &lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Kombination&#039;&#039;&#039;                      ||   2V  ||   2G  ||  2W   ||  2H   ||  2J   ||  3A&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  || 350  || 400  || 450  || 500  || 630  || 1000 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Case Code&#039;&#039;&#039; (Bauform)      ||  A  ||  B  ||  C  ||  D   &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EIA Code&#039;&#039;&#039;                 ||  3216 || 3528  || 6032  ||  7343&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Länge&#039;&#039;&#039; (mm)               || 3,2 || 3,5 || 6,0 || 7,3 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Breite&#039;&#039;&#039; (mm)              || 1,6 || 2,8 || 3,2 || 4,3 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Höhe&#039;&#039;&#039; (mm)                || 1,6 || 1,9 || 2,5 || 2,8 &lt;br /&gt;
|-&lt;br /&gt;
|    Maßtoleranz (mm)            || 0,2 || 0,2 || 0,3 || 0,3  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Siebkondensator ===&lt;br /&gt;
&lt;br /&gt;
Der Siebkondensator sitzt hinter einem Gleichrichter und hat die Aufgabe, aus einer gleichgerichteten, pulsierenden Spannung, eine annähernd konstante Gleichspannung mit nur wenig Welligkeit (engl. Ripple) zu machen. Er wird periodisch geladen und muss während der Ladepausen, wenn die Eingangssspannung vor dem Gleichrichter kleiner als die Ausgangsspannung ist, den Ausgang mit Strom versorgen. Man findet ihn in allen klassischen Netzteilen mit 50Hz Trafo. Als Daumenregel kann man sich merken, dass man pro 1A Ausgangsstrom ca. 10mF braucht, um eine Welligkeit von ca. 1Vpp zu erreichen.&lt;br /&gt;
&lt;br /&gt;
Einen Siebkondensator findet man auch am Ausgang von Schaltnetzteilen, seine Aufgabe ist dort die gleiche. Allerdings sind die Schaltfrequenzen deutlich höher, typisch 50-500kHz. Darum muss dieser Kondensator einen besonders kleinen, effektiven Innenwiderstand besitzen (engl. ESR, Equivalent Series Resistance).&lt;br /&gt;
&lt;br /&gt;
=== Entkoppelkondensator ===&lt;br /&gt;
&lt;br /&gt;
Der Entkoppelkondensator hat die Aufgabe, die Versorgungsspannung nahe an einem IC für hochfrequente Ströme zu puffern (entkoppeln, engl. decoupling). Schnelle Digital- und Analogschaltungen benötigen vor allem beim Umschalten sehr schnell viel Strom, in der Größenordnung von Nanosekunden bis Mikrosekunden, je nach IC Milliampere bis Ampere. Diese müssen mit möglichst geringem Widerstand und Induktivität geliefert werden. Ein Stromversorgungsnetz auf einer Platine kann das meist nur unzureichend, dazu sind die Leitungen meist zu lang und damit die Induktivität zu hoch. Ein nah am IC platzierter Kondensator liefert diesen Strom für kurze Zeit, ohne dass die Spannung nennenswert einbricht. Die Entkopplung der Stromversorgung geschieht meist mehrstufig, d.h. es werden Kondensatoren verschiedener Arten und Kapazitäten eingesetzt, siehe [[Stromversorgung für FPGAs]].&lt;br /&gt;
&lt;br /&gt;
Praktische Anwendung&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Jeder&#039;&#039;&#039; Digitalschaltkreis benötigt einen 100nF Keramikkondensator nah (kleiner 20mm) an den Anschlüssen von VCC und GND. Je schneller der IC schalten kann, umso wichtiger ist er und umso näher muss er platziert werden. Ein &amp;quot;langsamer&amp;quot; IC wie ein AVR Mikrocontroller verkraftet 20mm, im Ausnahmefall auch mehr. Ein deutlich schnellerer IC wie moderne FPGAs, große CPUs, sehr schnelle ASICs etc. sind da anspruchsvoller, vor allem wenn die Schaltung stabil laufen soll. Dann muss man möglichst jeden Millimeter Leiterbahn zwischen Pin und Kondensator einsparen, soweit das mechanisch geht.&lt;br /&gt;
* Für &#039;&#039;&#039;jedes&#039;&#039;&#039; Anschlusspaar von VCC und GND eines ICs muss ein Kondensator verwendet werden. Sparen geht hier oft schief!&lt;br /&gt;
* Für die Verbindung der Entkoppelkondensatoren zur Masse- bzw. Versorgungsfläche sollte man möglichst je Kondensator ein VIA benutzen und nicht über ein VIA mehrere Kondensatoren verbinden. Dadurch wird die parasitäre Induktivität vermindert.&lt;br /&gt;
* Schnelle Analogschaltkreise wie Operationsverstärker, [[Treiber]] etc. brauchen auch individuelle Entkoppelkondensatoren.&lt;br /&gt;
* Pi mal Daumen gilt: Je größer der Kondensator, umso weiter kann er von dem Verbraucher weg sein, da er auf Grund seines Innenwiderstands (ESR, ESL) weniger HF-tauglich ist. Es ist somit nicht sinnvoll, einen 1000µF Elektrolytkondensator 10mm neben einen Digital-IC setzen zu wollen. Dort gehört der 100nF Keramikkondensator hin. Aber für die Stromversorgung von Motoren, Treibern und [[H-Brücken Übersicht|H-Brücken]] sind derartige großen Kondensatoren wichtig und sollten nicht zu weit entfernt sein.&lt;br /&gt;
&lt;br /&gt;
=== Koppelkondensator ===&lt;br /&gt;
&lt;br /&gt;
Koppelkondensatoren verbinden Verstärkerstufen. Dabei wird jedoch nur der Wechselanteil übertragen, kein Gleichanteil. Diese Kondensatoren müssen möglichst verzerrungsarm sein, vor allem im Audiobereich. Das wird durch die richtige Wahl des Dielektrikums erreicht.&lt;br /&gt;
&lt;br /&gt;
=== Forumsbeiträge ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/43762 Abblockkondensator: Kerko (Keramik) oder Folie?]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/62576 Brennende Tantalkondensatoren]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/108865#963281 Kodierung (Umwandlung)]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/169958#1624840 Unterschied: Elektrolytkondensator vs. Tantalkondensator]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/195563#1915294 Unterschied: C0G vs. Glimmer]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/264347#2748845 Kurze Beschreibung verschiedener Folienkondensatoren]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/108865#1356075 PC Tool zum Identifizieren von Kondensatoren]&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/436472#5164488 Kräftige Ladepumpe 12V?], wie Ladungspumpen effizient arbeiten&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/470224#5763932 MMLC Kondensatoren Bauform reduzieren]&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/440901#5264220 1F-Kondensator laden &amp;amp; entladen]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://my.execpc.com/~endlr/index.html CapSite 2009] - Introduction To Capacitors&lt;br /&gt;
* [http://www.cde.com/catalogs/AEappGUIDE.pdf Aluminum Electrolytic Capacitor Application Guide] von CDE Cornell Dubilier&lt;br /&gt;
* Sehr ausf&amp;amp;uuml;hrliche Artikel bei Wikipedia&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Elektrolytkondensator Elektrolytkondensator]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Polymer-Elektrolytkondensator Polymer-Elkos]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Aluminium-Elektrolytkondensator Al-(Polymer-)Elkos]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Tantal-Elektrolytkondensator Niob- und Tantal-(Polymer-)Elkos]&lt;br /&gt;
* Anwendungen&lt;br /&gt;
** [http://www.radio-electronics.com/info/data/capacitor/capacitor_types.php Capacitor types and their uses] (engl.)&lt;br /&gt;
** [http://www.planetanalog.com/features/showArticle.jhtml;jsessionid=23?articleID=199905522 Choosing and Using Bypass Capacitors] (dreiteilige Artikelserie) bei [http://www.planetanalog.com www.planetanalog.com]&lt;br /&gt;
** [http://www.vagrearg.org/?p=decoupling Decoupling by Example]&lt;br /&gt;
* Video-Tutorials&lt;br /&gt;
** EEVblog #33 – Capacitor Tutorial [http://www.eevblog.com/2009/09/26/eevblog-33-1of2-capacitor-tutorial-electrolytic-tantalum-and-plastic-film/ Teil 1: Electrolytic, Tantalum, and Plastic Film] und [http://www.eevblog.com/2009/09/26/eevblog-33-2of2-capacitor-tutorial-ceramics-and-impedance/ Teil 2: Ceramics and impedance]&lt;br /&gt;
* [http://www.maximintegrated.com/app-notes/index.mvp/id/5527 Tutorial] von Maxim, &amp;quot;Temperature and Voltage Variation of Ceramic Capacitors, or Why Your 4.7µF Capacitor Becomes a 0.33µF Capacitor&amp;quot;, engl.&lt;br /&gt;
* [http://www.wima.de/DE/characteristics.htm Vergleichstabelle] der Eigenschaften von Folien- und Keramikkondensatoren, WIMA&lt;br /&gt;
* [http://portal.national.com/rap/Application/0,1570,28,00.html] Bob Pease: &amp;quot;Understand Capacitor Soakage to Optimize Analog Systems&amp;quot;&lt;br /&gt;
* [http://www.youtube.com/watch?v=AP_FSyWGpoE Youtube-Video zum Thema Abblockkondensatoren (Deutsch)]&lt;br /&gt;
* [http://www.lothar-miller.de/s9y/categories/14-Entkopplung Entkopplung] von ICs, von Lothar Miller&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Kondensator&amp;diff=107596</id>
		<title>Kondensator</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Kondensator&amp;diff=107596"/>
		<updated>2025-08-04T08:25:40Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* Folien-Kondensator */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein &#039;&#039;&#039;Kondensator&#039;&#039;&#039; ist ein passives elektrisches Bauteil mit zwei Anschlüssen, welches elektrische Energie in einem elektrischen Feld in Form von Ladung speichert. Die charakteristische Größe, die den Kondensator beschreibt, ist die Kapazität C, gemessen in Farad (F). Ein Kondensator besteht aus zwei Flächen gut leitfähigen Materials, die voneinander isoliert sind. Das Isolationsmaterial ist ein Dielektrikum und verleiht dem Kondensator je nach Material stark unterschiedliche Eigenschaften.&lt;br /&gt;
&lt;br /&gt;
== Aufbau eines Kondensators ==&lt;br /&gt;
Je größer die Fläche, je geringer der Abstand der Flächen und je höher die relative Permittivität \varepsilon_r - das Dielektrikum - desto höher ist die Kapazität des Bauteils. Der einfachste Kondensator besteht aus zwei voneinander isolierten Metallplatten, zwischen denen sich Luft als &amp;quot;Dielektrikum&amp;quot; befindet. Um die Fläche und damit die Kapazität zu vergrößern gibt es verschiedene Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
=== Wickelkondensator ===&lt;br /&gt;
Die einfachste Methode die Flächen zu vergrößern ist es, zwei leitfähige Folien mit einem Isolationsmaterial dazwischen aufzuwickeln. Diese Methode wird für so ziemlich alle Kondensatortypen verwendet. Manchmal wird der Wickel nach der Herstellung flachgepresst, um das Volumen zu optimieren.&lt;br /&gt;
&lt;br /&gt;
=== Schichtkondensator ===&lt;br /&gt;
Diese Aufbauart wird durch &amp;quot;stapeln&amp;quot; der unterschiedlichen Lagen erreicht. Verfahrenstechnisch ist dies oft wieder ein Wickel, jedoch meist auf bis zu 2 Meter durchmessenden Wickelrädern. Dieses ursprünglich von Siemens eingesetzte Verfahren wird auch aktuell noch verwendet, hauptsächlich für Folienkondensatoren bis in den mF-Bereich hinein. Nach dem Wickeln auf vieleckigen Rädern wird der Folienstapel geschnitten und auf die gewünschte Größe = Kapazität getrennt.&lt;br /&gt;
&lt;br /&gt;
=== Zusammenhang Baugröße, Spannung, Kapazität ===&lt;br /&gt;
Abgesehen vom Einfluss des Dielektrikums geht das Bauvolumen grundsätzlich linear zur Kapazität und quadratisch(!) zur Spannungsfestigkeit. Denn zur Verdopplung der Kapazität verdoppelt man die Kondensatorfläche, entsprechend einer Parallelschaltung von Kondensatoren. Zur Verdopplung der Spannungsfestigkeit wird wie bei einer Reihenschaltung die Dicke des Dielektrikums verdoppelt, was mit einer Halbierung der Kapazität einhergeht: Bei gleicher Kapazität muss daher auch die Kondensatorfläche verdoppelt werden.&lt;br /&gt;
&lt;br /&gt;
Somit ist das Bauvolumen direkt proportional zum maximalen Energiegehalt des Kondensators.&lt;br /&gt;
&lt;br /&gt;
== Kondensatortypen ==&lt;br /&gt;
(Vakuum-, Glimmer-, Gas- und Glaskondensatoren nicht berücksichtigt)&lt;br /&gt;
=== Keramik-Kondensator ===&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Keramikkondensator in [https://de.wikipedia.org/wiki/Keramikkondensator#Spannungsabh.C3.A4ngigkeit_der_Kapazit.C3.A4t Wikipedia]&#039;&#039;&#039;. Es gibt sie bedrahtet und in SMD. Bedrahtet gab es sie früher als Einschicht-Kapazität in Scheibenform, heutzutage nur noch als Vielschicht-Kondensator. Einschichtig nur für Spannungen im Kilovolt-Bereich.&lt;br /&gt;
&lt;br /&gt;
Kerkos sind sogenannte &amp;quot;Vielschicht-Kondensatoren&amp;quot; die aus mehreren hundert Lagen einer isolierenden Keramik (Titandioxid+Bariumtitanat), und einer elektrisch leitfähigen Metallisierung (Aluminium &amp;amp; Magnesiumsilikate) bestehen.&lt;br /&gt;
Dieser Typ ist wie der Folienkondensator für höchste Frequenzen geeignet, hat eine sehr geringe Baugröße, eine gute Temperaturstabilität aber piezoelektrische Eigenschaften. Die Kapazität pro mm³ variiert je nach Dielektrikum Z5U, Y5V, X7R, C0 (in abnehmender Kapazitätsdichte) und ist sowohl abhängig von Temperatur als auch der angelegten Spannung.&lt;br /&gt;
&lt;br /&gt;
Keramik-Kondensatoren sind sehr preisgünstig und werden überall dort eingesetzt, wo andere Kondensatortypen keinen Vorteil bringen. Zudem lösen sie mehr und mehr Elektrolytkondensatoren ab, die ihrerseits mit hoher Kapazität punkten: Die Lebensdauer, besonders bei hoher Temperatur, ist höher.&lt;br /&gt;
&lt;br /&gt;
=== Folien-Kondensator ===&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Folienkondensator in [https://de.wikipedia.org/wiki/Kunststoff-Folienkondensator Wikipedia]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Unterschiedliche Folienarten verleihen dem Kondensator stark unterschiedliche Eigenschaften wie z.B. temperatur- und alterungsbeständigkeit, Verlustfaktor, Isolationswiderstand, und die obere Grenzfrequenz, um nur die wichtigsten zu nennen. Die Dicke der Metallisierung hingegen entscheidet über den maximal zulässigen Rippelstrom / Impulsstromfestigkeit und darüber, ob sich ein Kondensator &amp;quot;selbst heilen&amp;quot; kann, oder nicht. Das Verfahren der &amp;quot;Selbstheilung&amp;quot; funktioniert nur bei Metallisierungen, da es darauf beruht, daß die im C gespeicherte Energie den Metallbelag beim Durchschlag durch die Isolation verdampfen kann. Ist die Energie zu gering, wird nur wenig Metall um den Durchschlag verdampft, und der Kurzschluß bleibt bestehen. Ist die Energie zu hoch, wird der Kondensator thermisch zerstört. Hat der C den richigen Energiegehalt, verdampft direkt um die Durchschlagstelle herum bis zu einigen mm der Metallisierung, und die Fehlstelle ist wieder isoliert. Im Übrigen ist eine Parallelsschaltung aus vielen,als selbstheilenden C&#039;s nicht unbedingt mehr &amp;quot;selbstheilend&amp;quot; da der Energieeintrag dann aus dem Gesamtverbund kommt, und oft zu hoch ist um nur eine kleine Fläche verdampfen zu lassen.&lt;br /&gt;
Impulsstrom-feste Kondensatoren zeichnen sich durch hohe Schichtstärken der Metallisierung aus, oder verwenden statt der Metallbeschichtung direkt eine Metallfolie. Hier wäre normalerweise ein &amp;quot;selbstheilen&amp;quot; gleichbedeutend mit einem so hohen Energieeintrag, daß das Bauteil zerstört wird.&lt;br /&gt;
&lt;br /&gt;
Hier eine kleine Übersicht mit der Bitte um Vervollständigung.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| style=&amp;quot;width:8em&amp;quot;| &#039;&#039;&#039;Folientyp&#039;&#039;&#039;  || Poly-&amp;lt;br&amp;gt;propylen || Poly-&amp;lt;br&amp;gt;styren || Poly-&amp;lt;br&amp;gt;carbonat  ||Poly-&amp;lt;br&amp;gt;ester|| Poly-&amp;lt;br&amp;gt;styrol  ||   Poly-&amp;lt;br&amp;gt;ethylen-&amp;lt;br&amp;gt;naphthalat ||   Polytetra-&amp;lt;br&amp;gt;fluor-&amp;lt;br&amp;gt;ethylen ||   Poly-&amp;lt;br&amp;gt;phenylen-&amp;lt;br&amp;gt;sulfid    ||   Metall-&amp;lt;br&amp;gt;Papier   &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Folien-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;markenname&#039;&#039;&#039; || Hostalen || -   || Makrofol, Makroflex ||  Mylar, Hostaphan ||   Styroflex ||  Kaladex||  Teflon|| Tedur, Ryton|| Papier&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Abkürzung&#039;&#039;&#039; || PP || -  || PC||  PET ||  PS||  PEN||  PTFE|| PPS|| -  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Typ&#039;&#039;&#039; || (F)KP, MKP || -KS, MKS   || KC, MKC ||  (F)KT, MKT  ||  -KS, MKS ||  (F)KN, MKN||  -|| (F)KI, MKI|| MP  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Temperatur-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /°C&#039;&#039;&#039; || ca 105 || -    || ca. 105 ||  125..150  ||  -  ||  150 ||  -|| 150 || 85  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Toleranz&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;+/-%&#039;&#039;&#039; || 1 || 1   || -||  1  ||  -  ||  -||  -|| -|| 20  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Grenzfrequenz /kHz&#039;&#039;&#039; || 100 || 400   || -||  -  ||  1000  ||  -||  -|| -|| -  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Selbstheilend&#039;&#039;&#039; || ja || -  || -||  ja  ||  -  ||  -||  -|| -|| ja  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Impuls-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;belastbarkeit&#039;&#039;&#039; || + || +   || ||  +  ||  -- ||  -||  -|| -|| +  &lt;br /&gt;
|}&lt;br /&gt;
(MK) = metallisierte Folie,  (F) = Metallbeläge z.B. Metallfolie (K) = Metallfolie+Kunststofffolie&lt;br /&gt;
&lt;br /&gt;
Für die Anwendung in Kondensatornetzteilen ist die Anwendung des Selbstheilungseffekts &#039;&#039;&#039;erforderlich&#039;&#039;&#039;. Daher findet man an dieser Stelle faktisch keine Keramikkondensatoren.&lt;br /&gt;
&lt;br /&gt;
=== Elektrolyt-Kondensator ===&lt;br /&gt;
Ein Elko unterscheidet sich hauptsächlich dadurch von anderen Kondensatoren, daß nur eine Elektrode aus einer Metallschicht besteht, die zweite aus einem festen, oder flüssigen Elektrolyten.&lt;br /&gt;
&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Elektrolytkondensator in [https://de.wikipedia.org/wiki/Elektrolytkondensator Wikipedia]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Fest-Elektrolyt ====&lt;br /&gt;
Diese Bauteile bestehen aus Polymeren, Metalloxiden, Braunstein, ... und sind wie die Elkos immer gepolt.&lt;br /&gt;
Als Metalloxide ist Tantal sehr, und Niob noch weniger verbreitet.&lt;br /&gt;
Takos sind &#039;&#039;&#039;SEHR&#039;&#039;&#039; empfindlich gegenüber schnellen Spannungsanstiegen bzw. Überspannung und sollten nie ohne Vorwiderstand direkt an einer niederohmigen Quelle (Batterie) geladen/betrieben werden. Takos verbrennen im Fehlerfall mit einer Stichflamme, daher empfiehlt es sich eine deutlich höhere Spannungsfestigkeit zu wählen und - bei direktem Anschluß an eine niederohmige Quelle - zusätzlich ein Lade-Vorwiderstand einzubauen. Treten zusätzlich hohe Temperaturen auf, ist die Spannungsfestigkeit mindestens zu verdoppeln.&lt;br /&gt;
Kondensatoren mit Niob statt Tantal sind weniger verbreitet, und noch sehr teuer. Ihr Vorteil ist z.B. die Unempfindlichkeit gegenüber schnellen Spannungsanstiegen, sie können direkt an jeder Quelle betrieben werden.&lt;br /&gt;
&lt;br /&gt;
==== Flüssig-Elektrolyt ====&lt;br /&gt;
Diese Bauteile bestehen aus zwei Lagen &#039;&#039;&#039;sehr stark angeätzter&#039;&#039;&#039; Aluminiumfolie - zur Vergrößerung der Oberfläche - und einem flüssigen (seltener &amp;quot;einem festen&amp;quot;) Elektrolyten. Die zweite Folie kontaktiert gleichzeitig den Elektrolyten. Der Elektrolyt ist häufig auf Alkoholen basierend, aus/mit Schwefelsäure oder Glykol.&lt;br /&gt;
Die Alufolie wird nach den ätzen anodisch oxidiert. Die Eloxal-Schicht ist mechanisch belastbar, und stellt die &#039;&#039;&#039;einzige&#039;&#039;&#039; Isolation zwischen den beiden Anschlüssen dar. Die Oxidation der Alufolie wird je nach Hersteller bei ca. 140..165% der Nennspannung durchgeführt. Die Oxidschicht baut sich durch den Elektrolyten bei langer Lagerung stark ab, daher sinkt die Spannungsfestigkeit eines Elkos mit seinem Alter. Alte Bauteile können - sofern noch Elektrolyt enthalten ist - durch spannungsrichtiges Anschließen an eine Spannungsquelle und langsamen erhöhen der Spannung bis über die Nennspannung hinaus wieder &amp;quot;formiert&amp;quot; werden. Achtung, hier besteht die Gefahr eines Kurzschlusses zwischen den Anschlüssen, daher hochohmigen Vorwiderstand verwenden!&lt;br /&gt;
Wird ein Elko verpolt betrieben, wird die Oxidschicht im Laufe der Zeit (Abhängig von Spannung und Temperatur) abgebaut, und der Kondensator schlägt durch.&lt;br /&gt;
&lt;br /&gt;
=== Doppelschicht-Kondensatoren ===&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Superkondensator in [https://de.wikipedia.org/wiki/Superkondensator Wikipedia]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Diese Bauteile werden unter Markennamen wie &amp;quot;Goldcap&amp;quot;, &amp;quot;Ultracap&amp;quot; oder &amp;quot;Supercap&amp;quot; vertrieben, besitzen meist ein flüssiges Elektrolyt haben aber nicht besonders viel mit den o.g. &amp;quot;Elektrolytkondensatoren&amp;quot; gemeinsam. Dieser Kondensator besitzt Eigenschaften eines Kondensators und einer Batterie. Der &amp;quot;Kondensator&amp;quot; ist hier aus (im Gegensatz zum Elko) zwei Lagen einer einseitig metallisierten Aktivkohle, einem Separator, und einem Elektrolyten. &lt;br /&gt;
Das verwendete Elektrolyt erfüllt unter definierten Betriebsbedingungen die Funktion eines zusätzlichen Dielektrikums, und trägt zusätzlich zur ohnehin schon sehr großen Fläche der Aktivkohle, durch seine extrem dünne Schichtdicke, zur sehr hohen Kapazität bei.&lt;br /&gt;
Im Gegensatz zu einer Batterie kann der Doppelschichtkondensator Energie sehr schnell aufnehmen und abgeben, und altert dabei nur im geringen Maße. 500000 Lade-/Entladezyklen sind erreichbar, wenn die Spannung pro Zelle nicht überschritten wird. Normal sind hier 2,3..2,5V.&lt;br /&gt;
&lt;br /&gt;
== Anwendungen ==&lt;br /&gt;
Hier wird für verschiedene Anwendungsfälle eine sinnvolle Lösung empfohlen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Anwendung&#039;&#039;&#039;  || &#039;&#039;&#039;Beispiel&#039;&#039;&#039; || &#039;&#039;&#039;Empfohlener Typ&#039;&#039;&#039;  ||&#039;&#039;&#039;Kommentar&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Stromversorgung || Netzteil|| Elko||&lt;br /&gt;
|- &lt;br /&gt;
| Stromversorgung || Zwischenkreis|| Elkos, Folienkondensatoren||&lt;br /&gt;
|-&lt;br /&gt;
| Datenerhalt || || Doppelschichtkondensator ||&lt;br /&gt;
|-&lt;br /&gt;
| Energiespeicher|| digitale Schaltungen|| Kerko || z.B. 47nF pro IC bzw. Vcc-Anschluß&lt;br /&gt;
|- &lt;br /&gt;
| Hohe Impulsenergie x00ms || Impulsschweißgerät, (capacitive discharge)|| Elko (Folie)||&lt;br /&gt;
|-&lt;br /&gt;
| Impulsfeste Kondensatoren || Coils shrinker, Coilgun, Railgun|| Folie||MKT10 oder noch besser FKT1&lt;br /&gt;
|-&lt;br /&gt;
| NF-Filter|| PWM als DAC || Tako/Elko/Kerko ||&lt;br /&gt;
|-&lt;br /&gt;
| HF-Filter|| Beispiel || Styroflex-Folie ||&lt;br /&gt;
|-&lt;br /&gt;
| Audio || Kopplung || Folie ||&lt;br /&gt;
|-&lt;br /&gt;
| Vorwiderstand || Motorkondensator || MP oder Folie ||&lt;br /&gt;
|-&lt;br /&gt;
| Vorwiderstand || Kondensatornetzteil|| Kerko/Folie || Achtung, nur X2-Typen!&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Mathematik zum Kondensator ==&lt;br /&gt;
&lt;br /&gt;
Die Größe eines Kondensators ist seine Kapazität (Formelzeichen &#039;&#039;C&#039;&#039;), die als Ladung (Formelzeichen &#039;&#039;Q&#039;&#039;) durch Spannung (Formelzeichen &#039;&#039;U&#039;&#039;) definiert ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{Q}{U}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Einheit für die Kapazität ist Farad:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; [C]=F=\frac{As}{V}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reihenschaltung ===&lt;br /&gt;
&lt;br /&gt;
Für eine Reihenschaltung von &#039;&#039;n&#039;&#039; Kondensatoren gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{C_{ges}}=\frac{1}{C_1}+\frac{1}{C_2}+\dots+\frac{1}{C_n}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_{ges}=U_1+U_2+\dots+U_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I_{ges}=I_1=I_2=\dots=I_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parallelschaltung ===&lt;br /&gt;
&lt;br /&gt;
Für eine Parallelschaltung von &#039;&#039;n&#039;&#039; Kondensatoren gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_{ges}=C_1+C_2+\dots+C_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_{ges}=U_1=U_2=\dots=U_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I_{ges}=I_1+I_2+\dots+I_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Gespeicherte Energie ===&lt;br /&gt;
&lt;br /&gt;
Die in einem Kondensator gespeicherte Energie lässt sich durch die Formel&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; W=\frac{C}{2} \cdot U^2 = \frac{1}{2 \cdot C} \cdot Q^2 = \frac{U}{2} \cdot Q &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
berechnen.&lt;br /&gt;
&lt;br /&gt;
== Praxis ==&lt;br /&gt;
&lt;br /&gt;
=== Polaritätszeichen ===&lt;br /&gt;
&lt;br /&gt;
Elektrolytkondensatoren (kurz Elkos) sind in der Regel gepolt, d. h. Gleichspannungspegel müssen in einer vorgeschriebenen Polarität angelegt werden, damit das Dielektrikum nicht zerstört wird.  Bei Aluminium-Elkos wird dabei generell der Minuspol gekennzeichnet, bei kleinen Bauformen mit einem Strich, bei größeren Bauformen befinden sich u. U. auch noch Minuszeichen in diesem Strich eingebettet. Bei Tantal-Elkos hingegen wird immer der Pluspol gekennzeichnet, sowohl bei SMD als auch THT-Bauformen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:SMD-Elkos.jpeg]]&lt;br /&gt;
&lt;br /&gt;
Die gezeigten Elkos haben folgende Werte (von links nach rechts):&lt;br /&gt;
&lt;br /&gt;
* 220 µF, Spannungsfestigkeit 6 V&lt;br /&gt;
* 100 µF (&amp;lt;math&amp;gt;10 \cdot 10^7&amp;lt;/math&amp;gt; pF), 16 V&lt;br /&gt;
* 22 µF, 10 V&lt;br /&gt;
* 1 µF, 35 V&lt;br /&gt;
* 2,2 µF (&amp;lt;math&amp;gt;22 \cdot 10^5&amp;lt;/math&amp;gt; pF), 10 V&lt;br /&gt;
&lt;br /&gt;
Die beim 100-µF-Kondensator zu findende Buchstabenschreibweise für die Spannungsfestigkeit ist wenig gebräuchlich, aber gelegentlich anzutreffen.  Die Zuordnung ist:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| style=&amp;quot;width:8em&amp;quot; | &#039;&#039;&#039;Buchstabe&#039;&#039;&#039; ||  e  ||   g   ||   j   ||   A   ||   C   ||   D   ||   E   ||   V   ||   H   ||   J   ||   K&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039; || 2.5 || 4   || 6,3 ||  10 ||  16 ||  20 ||  25 || 35 || 50 || 63 || 80&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Spannungsfestigkeit kann auch durch eine zweistellige Kombination aus einer Ziffer und einem Buchstaben codiert sein:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| style=&amp;quot;width:8em&amp;quot; | &#039;&#039;&#039;Kombination&#039;&#039;&#039;  ||   0G  ||   0L  ||  0J   ||  1A   ||  1C   ||  1E &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  || 4,0  || 5,5  || 6,3  ||  10  ||  16  ||  20 &lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Kombination&#039;&#039;&#039;                      ||   1H  ||   1J  ||  1K   ||  2A   ||  2Q   ||  2B &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  ||  50  ||  63  ||  80  || 100  || 110  || 125 &lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Kombination&#039;&#039;&#039;                      ||   2C  ||   2Z  ||  2D   ||  2P   ||  2E   ||  2F&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  || 160  || 180  || 200  || 220  || 250  || 315 &lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Kombination&#039;&#039;&#039;                      ||   2V  ||   2G  ||  2W   ||  2H   ||  2J   ||  3A&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  || 350  || 400  || 450  || 500  || 630  || 1000 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Case Code&#039;&#039;&#039; (Bauform)      ||  A  ||  B  ||  C  ||  D   &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EIA Code&#039;&#039;&#039;                 ||  3216 || 3528  || 6032  ||  7343&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Länge&#039;&#039;&#039; (mm)               || 3,2 || 3,5 || 6,0 || 7,3 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Breite&#039;&#039;&#039; (mm)              || 1,6 || 2,8 || 3,2 || 4,3 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Höhe&#039;&#039;&#039; (mm)                || 1,6 || 1,9 || 2,5 || 2,8 &lt;br /&gt;
|-&lt;br /&gt;
|    Maßtoleranz (mm)            || 0,2 || 0,2 || 0,3 || 0,3  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Siebkondensator ===&lt;br /&gt;
&lt;br /&gt;
Der Siebkondensator sitzt hinter einem Gleichrichter und hat die Aufgabe, aus einer gleichgerichteten, pulsierenden Spannung, eine annähernd konstante Gleichspannung mit nur wenig Welligkeit (engl. Ripple) zu machen. Er wird periodisch geladen und muss während der Ladepausen, wenn die Eingangssspannung vor dem Gleichrichter kleiner als die Ausgangsspannung ist, den Ausgang mit Strom versorgen. Man findet ihn in allen klassischen Netzteilen mit 50Hz Trafo. Als Daumenregel kann man sich merken, dass man pro 1A Ausgangsstrom ca. 10mF braucht, um eine Welligkeit von ca. 1Vpp zu erreichen.&lt;br /&gt;
&lt;br /&gt;
Einen Siebkondensator findet man auch am Ausgang von Schaltnetzteilen, seine Aufgabe ist dort die gleiche. Allerdings sind die Schaltfrequenzen deutlich höher, typisch 50-500kHz. Darum muss dieser Kondensator einen besonders kleinen, effektiven Innenwiderstand besitzen (engl. ESR, Equivalent Series Resistance).&lt;br /&gt;
&lt;br /&gt;
=== Entkoppelkondensator ===&lt;br /&gt;
&lt;br /&gt;
Der Entkoppelkondensator hat die Aufgabe, die Versorgungsspannung nahe an einem IC für hochfrequente Ströme zu puffern (entkoppeln, engl. decoupling). Schnelle Digital- und Analogschaltungen benötigen vor allem beim Umschalten sehr schnell viel Strom, in der Größenordnung von Nanosekunden bis Mikrosekunden, je nach IC Milliampere bis Ampere. Diese müssen mit möglichst geringem Widerstand und Induktivität geliefert werden. Ein Stromversorgungsnetz auf einer Platine kann das meist nur unzureichend, dazu sind die Leitungen meist zu lang und damit die Induktivität zu hoch. Ein nah am IC platzierter Kondensator liefert diesen Strom für kurze Zeit, ohne dass die Spannung nennenswert einbricht. Die Entkopplung der Stromversorgung geschieht meist mehrstufig, d.h. es werden Kondensatoren verschiedener Arten und Kapazitäten eingesetzt, siehe [[Stromversorgung für FPGAs]].&lt;br /&gt;
&lt;br /&gt;
Praktische Anwendung&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Jeder&#039;&#039;&#039; Digitalschaltkreis benötigt einen 100nF Keramikkondensator nah (kleiner 20mm) an den Anschlüssen von VCC und GND. Je schneller der IC schalten kann, umso wichtiger ist er und umso näher muss er platziert werden. Ein &amp;quot;langsamer&amp;quot; IC wie ein AVR Mikrocontroller verkraftet 20mm, im Ausnahmefall auch mehr. Ein deutlich schnellerer IC wie moderne FPGAs, große CPUs, sehr schnelle ASICs etc. sind da anspruchsvoller, vor allem wenn die Schaltung stabil laufen soll. Dann muss man möglichst jeden Millimeter Leiterbahn zwischen Pin und Kondensator einsparen, soweit das mechanisch geht.&lt;br /&gt;
* Für &#039;&#039;&#039;jedes&#039;&#039;&#039; Anschlusspaar von VCC und GND eines ICs muss ein Kondensator verwendet werden. Sparen geht hier oft schief!&lt;br /&gt;
* Für die Verbindung der Entkoppelkondensatoren zur Masse- bzw. Versorgungsfläche sollte man möglichst je Kondensator ein VIA benutzen und nicht über ein VIA mehrere Kondensatoren verbinden. Dadurch wird die parasitäre Induktivität vermindert.&lt;br /&gt;
* Schnelle Analogschaltkreise wie Operationsverstärker, [[Treiber]] etc. brauchen auch individuelle Entkoppelkondensatoren.&lt;br /&gt;
* Pi mal Daumen gilt: Je größer der Kondensator, umso weiter kann er von dem Verbraucher weg sein, da er auf Grund seines Innenwiderstands (ESR, ESL) weniger HF-tauglich ist. Es ist somit nicht sinnvoll, einen 1000µF Elektrolytkondensator 10mm neben einen Digital-IC setzen zu wollen. Dort gehört der 100nF Keramikkondensator hin. Aber für die Stromversorgung von Motoren, Treibern und [[H-Brücken Übersicht|H-Brücken]] sind derartige großen Kondensatoren wichtig und sollten nicht zu weit entfernt sein.&lt;br /&gt;
&lt;br /&gt;
=== Koppelkondensator ===&lt;br /&gt;
&lt;br /&gt;
Koppelkondensatoren verbinden Verstärkerstufen. Dabei wird jedoch nur der Wechselanteil übertragen, kein Gleichanteil. Diese Kondensatoren müssen möglichst verzerrungsarm sein, vor allem im Audiobereich. Das wird durch die richtige Wahl des Dielektrikums erreicht.&lt;br /&gt;
&lt;br /&gt;
=== Forumsbeiträge ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/43762 Abblockkondensator: Kerko (Keramik) oder Folie?]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/62576 Brennende Tantalkondensatoren]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/108865#963281 Kodierung (Umwandlung)]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/169958#1624840 Unterschied: Elektrolytkondensator vs. Tantalkondensator]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/195563#1915294 Unterschied: C0G vs. Glimmer]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/264347#2748845 Kurze Beschreibung verschiedener Folienkondensatoren]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/108865#1356075 PC Tool zum Identifizieren von Kondensatoren]&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/436472#5164488 Kräftige Ladepumpe 12V?], wie Ladungspumpen effizient arbeiten&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/470224#5763932 MMLC Kondensatoren Bauform reduzieren]&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/440901#5264220 1F-Kondensator laden &amp;amp; entladen]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://my.execpc.com/~endlr/index.html CapSite 2009] - Introduction To Capacitors&lt;br /&gt;
* [http://www.cde.com/catalogs/AEappGUIDE.pdf Aluminum Electrolytic Capacitor Application Guide] von CDE Cornell Dubilier&lt;br /&gt;
* Sehr ausf&amp;amp;uuml;hrliche Artikel bei Wikipedia&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Elektrolytkondensator Elektrolytkondensator]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Polymer-Elektrolytkondensator Polymer-Elkos]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Aluminium-Elektrolytkondensator Al-(Polymer-)Elkos]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Tantal-Elektrolytkondensator Niob- und Tantal-(Polymer-)Elkos]&lt;br /&gt;
* Anwendungen&lt;br /&gt;
** [http://www.radio-electronics.com/info/data/capacitor/capacitor_types.php Capacitor types and their uses] (engl.)&lt;br /&gt;
** [http://www.planetanalog.com/features/showArticle.jhtml;jsessionid=23?articleID=199905522 Choosing and Using Bypass Capacitors] (dreiteilige Artikelserie) bei [http://www.planetanalog.com www.planetanalog.com]&lt;br /&gt;
** [http://www.vagrearg.org/?p=decoupling Decoupling by Example]&lt;br /&gt;
* Video-Tutorials&lt;br /&gt;
** EEVblog #33 – Capacitor Tutorial [http://www.eevblog.com/2009/09/26/eevblog-33-1of2-capacitor-tutorial-electrolytic-tantalum-and-plastic-film/ Teil 1: Electrolytic, Tantalum, and Plastic Film] und [http://www.eevblog.com/2009/09/26/eevblog-33-2of2-capacitor-tutorial-ceramics-and-impedance/ Teil 2: Ceramics and impedance]&lt;br /&gt;
* [http://www.maximintegrated.com/app-notes/index.mvp/id/5527 Tutorial] von Maxim, &amp;quot;Temperature and Voltage Variation of Ceramic Capacitors, or Why Your 4.7µF Capacitor Becomes a 0.33µF Capacitor&amp;quot;, engl.&lt;br /&gt;
* [http://www.wima.de/DE/characteristics.htm Vergleichstabelle] der Eigenschaften von Folien- und Keramikkondensatoren, WIMA&lt;br /&gt;
* [http://portal.national.com/rap/Application/0,1570,28,00.html] Bob Pease: &amp;quot;Understand Capacitor Soakage to Optimize Analog Systems&amp;quot;&lt;br /&gt;
* [http://www.youtube.com/watch?v=AP_FSyWGpoE Youtube-Video zum Thema Abblockkondensatoren (Deutsch)]&lt;br /&gt;
* [http://www.lothar-miller.de/s9y/categories/14-Entkopplung Entkopplung] von ICs, von Lothar Miller&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Kondensator&amp;diff=107595</id>
		<title>Kondensator</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Kondensator&amp;diff=107595"/>
		<updated>2025-08-04T08:22:31Z</updated>

		<summary type="html">&lt;p&gt;Heha: Proportionalitätsbetrachtung&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein &#039;&#039;&#039;Kondensator&#039;&#039;&#039; ist ein passives elektrisches Bauteil mit zwei Anschlüssen, welches elektrische Energie in einem elektrischen Feld in Form von Ladung speichert. Die charakteristische Größe, die den Kondensator beschreibt, ist die Kapazität C, gemessen in Farad (F). Ein Kondensator besteht aus zwei Flächen gut leitfähigen Materials, die voneinander isoliert sind. Das Isolationsmaterial ist ein Dielektrikum und verleiht dem Kondensator je nach Material stark unterschiedliche Eigenschaften.&lt;br /&gt;
&lt;br /&gt;
== Aufbau eines Kondensators ==&lt;br /&gt;
Je größer die Fläche, je geringer der Abstand der Flächen und je höher die relative Permittivität \varepsilon_r - das Dielektrikum - desto höher ist die Kapazität des Bauteils. Der einfachste Kondensator besteht aus zwei voneinander isolierten Metallplatten, zwischen denen sich Luft als &amp;quot;Dielektrikum&amp;quot; befindet. Um die Fläche und damit die Kapazität zu vergrößern gibt es verschiedene Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
=== Wickelkondensator ===&lt;br /&gt;
Die einfachste Methode die Flächen zu vergrößern ist es, zwei leitfähige Folien mit einem Isolationsmaterial dazwischen aufzuwickeln. Diese Methode wird für so ziemlich alle Kondensatortypen verwendet. Manchmal wird der Wickel nach der Herstellung flachgepresst, um das Volumen zu optimieren.&lt;br /&gt;
&lt;br /&gt;
=== Schichtkondensator ===&lt;br /&gt;
Diese Aufbauart wird durch &amp;quot;stapeln&amp;quot; der unterschiedlichen Lagen erreicht. Verfahrenstechnisch ist dies oft wieder ein Wickel, jedoch meist auf bis zu 2 Meter durchmessenden Wickelrädern. Dieses ursprünglich von Siemens eingesetzte Verfahren wird auch aktuell noch verwendet, hauptsächlich für Folienkondensatoren bis in den mF-Bereich hinein. Nach dem Wickeln auf vieleckigen Rädern wird der Folienstapel geschnitten und auf die gewünschte Größe = Kapazität getrennt.&lt;br /&gt;
&lt;br /&gt;
=== Zusammenhang Baugröße, Spannung, Kapazität ===&lt;br /&gt;
Abgesehen vom Einfluss des Dielektrikums geht das Bauvolumen grundsätzlich linear zur Kapazität und quadratisch(!) zur Spannungsfestigkeit. Denn zur Verdopplung der Kapazität verdoppelt man die Kondensatorfläche, entsprechend einer Parallelschaltung von Kondensatoren. Zur Verdopplung der Spannungsfestigkeit wird wie bei einer Reihenschaltung die Dicke des Dielektrikums verdoppelt, was mit einer Halbierung der Kapazität einhergeht: Bei gleicher Kapazität muss daher auch die Kondensatorfläche verdoppelt werden.&lt;br /&gt;
&lt;br /&gt;
Somit ist das Bauvolumen direkt proportional zum maximalen Energiegehalt des Kondensators.&lt;br /&gt;
&lt;br /&gt;
== Kondensatortypen ==&lt;br /&gt;
(Vakuum-, Glimmer-, Gas- und Glaskondensatoren nicht berücksichtigt)&lt;br /&gt;
=== Keramik-Kondensator ===&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Keramikkondensator in [https://de.wikipedia.org/wiki/Keramikkondensator#Spannungsabh.C3.A4ngigkeit_der_Kapazit.C3.A4t Wikipedia]&#039;&#039;&#039;. Es gibt sie bedrahtet und in SMD. Bedrahtet gab es sie früher als Einschicht-Kapazität in Scheibenform, heutzutage nur noch als Vielschicht-Kondensator. Einschichtig nur für Spannungen im Kilovolt-Bereich.&lt;br /&gt;
&lt;br /&gt;
Kerkos sind sogenannte &amp;quot;Vielschicht-Kondensatoren&amp;quot; die aus mehreren hundert Lagen einer isolierenden Keramik (Titandioxid+Bariumtitanat), und einer elektrisch leitfähigen Metallisierung (Aluminium &amp;amp; Magnesiumsilikate) bestehen.&lt;br /&gt;
Dieser Typ ist wie der Folienkondensator für höchste Frequenzen geeignet, hat eine sehr geringe Baugröße, eine gute Temperaturstabilität aber piezoelektrische Eigenschaften. Die Kapazität pro mm³ variiert je nach Dielektrikum Z5U, Y5V, X7R, C0 (in abnehmender Kapazitätsdichte) und ist sowohl abhängig von Temperatur als auch der angelegten Spannung.&lt;br /&gt;
&lt;br /&gt;
Keramik-Kondensatoren sind sehr preisgünstig und werden überall dort eingesetzt, wo andere Kondensatortypen keinen Vorteil bringen. Zudem lösen sie mehr und mehr Elektrolytkondensatoren ab, die ihrerseits mit hoher Kapazität punkten: Die Lebensdauer, besonders bei hoher Temperatur, ist höher.&lt;br /&gt;
&lt;br /&gt;
=== Folien-Kondensator ===&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Folienkondensator in [https://de.wikipedia.org/wiki/Kunststoff-Folienkondensator Wikipedia]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Unterschiedliche Folienarten verleihen dem Kondensator stark unterschiedliche Eigenschaften wie z.B. temperatur- und alterungsbeständigkeit, Verlustfaktor, Isolationswiderstand, und die obere Grenzfrequenz, um nur die wichtigsten zu nennen. Die Dicke der Metallisierung hingegen entscheidet über den maximal zulässigen Rippelstrom / Impulsstromfestigkeit und darüber, ob sich ein Kondensator &amp;quot;selbst heilen&amp;quot; kann, oder nicht. Das Verfahren der &amp;quot;Selbstheilung&amp;quot; funktioniert nur bei Metallisierungen, da es darauf beruht, daß die im C gespeicherte Energie den Metallbelag beim Durchschlag durch die Isolation verdampfen kann. Ist die Energie zu gering, wird nur wenig Metall um den Durchschlag verdampft, und der Kurzschluß bleibt bestehen. Ist die Energie zu hoch, wird der Kondensator thermisch zerstört. Hat der C den richigen Energiegehalt, verdampft direkt um die Durchschlagstelle herum bis zu einigen mm der Metallisierung, und die Fehlstelle ist wieder isoliert. Im Übrigen ist eine Parallelsschaltung aus vielen,als selbstheilenden C&#039;s nicht unbedingt mehr &amp;quot;selbstheilend&amp;quot; da der Energieeintrag dann aus dem Gesamtverbund kommt, und oft zu hoch ist um nur eine kleine Fläche verdampfen zu lassen.&lt;br /&gt;
Impulsstrom-feste Kondensatoren zeichnen sich durch hohe Schichtstärken der Metallisierung aus, oder verwenden statt der Metallbeschichtung direkt eine Metallfolie. Hier wäre normalerweise ein &amp;quot;selbstheilen&amp;quot; gleichbedeutend mit einem so hohen Energieeintrag, daß das Bauteil zerstört wird.&lt;br /&gt;
&lt;br /&gt;
Hier eine kleine Übersicht mit der Bitte um Vervollständigung.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| style=&amp;quot;width:8em&amp;quot;| &#039;&#039;&#039;Folientyp&#039;&#039;&#039;  || Poly-&amp;lt;br&amp;gt;propylen || Poly-&amp;lt;br&amp;gt;styren || Poly-&amp;lt;br&amp;gt;carbonat  ||Poly-&amp;lt;br&amp;gt;ester|| Poly-&amp;lt;br&amp;gt;styrol  ||   Poly-&amp;lt;br&amp;gt;ethylen-&amp;lt;br&amp;gt;naphthalat ||   Polytetra-&amp;lt;br&amp;gt;fluor-&amp;lt;br&amp;gt;ethylen ||   Poly-&amp;lt;br&amp;gt;phenylen-&amp;lt;br&amp;gt;sulfid    ||   Metall-&amp;lt;br&amp;gt;Papier   &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Folien-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;markenname&#039;&#039;&#039; || Hostalen || -   || Makrofol, Makroflex ||  Mylar, Hostaphan ||   Styroflex ||  Kaladex||  Teflon|| Tedur, Ryton|| Papier&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Abkürzung&#039;&#039;&#039; || PP || -  || PC||  PET ||  PS||  PEN||  PTFE|| PPS|| -  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Typ&#039;&#039;&#039; || (F)KP, MKP || -KS, MKS   || KC, MKC ||  (F)KT, MKT  ||  -KS, MKS ||  (F)KN, MKN||  -|| (F)KI, MKI|| MP  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Temperatur-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /°C&#039;&#039;&#039; || ca 105 || -    || ca. 105 ||  125..150  ||  -  ||  150 ||  -|| 150 || 85  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Toleranz&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;+/-%&#039;&#039;&#039; || 1 || 1   || -||  1  ||  -  ||  -||  -|| -|| 20  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Grenzfrequenz /kHz&#039;&#039;&#039; || 100 || 400   || -||  -  ||  1000  ||  -||  -|| -|| -  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Selbstheilend&#039;&#039;&#039; || ja || -  || -||  ja  ||  -  ||  -||  -|| -|| ja  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Impuls-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;belastbarkeit&#039;&#039;&#039; || + || +   || ||  +  ||  -- ||  -||  -|| -|| +  &lt;br /&gt;
|}&lt;br /&gt;
(MK) = metallisierte Folie,  (F) = Metallbeläge z.B. Metallfolie (K) = Metallfolie+Kunststofffolie&lt;br /&gt;
&lt;br /&gt;
=== Elektrolyt-Kondensator ===&lt;br /&gt;
Ein Elko unterscheidet sich hauptsächlich dadurch von anderen Kondensatoren, daß nur eine Elektrode aus einer Metallschicht besteht, die zweite aus einem festen, oder flüssigen Elektrolyten.&lt;br /&gt;
&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Elektrolytkondensator in [https://de.wikipedia.org/wiki/Elektrolytkondensator Wikipedia]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Fest-Elektrolyt ====&lt;br /&gt;
Diese Bauteile bestehen aus Polymeren, Metalloxiden, Braunstein, ... und sind wie die Elkos immer gepolt.&lt;br /&gt;
Als Metalloxide ist Tantal sehr, und Niob noch weniger verbreitet.&lt;br /&gt;
Takos sind &#039;&#039;&#039;SEHR&#039;&#039;&#039; empfindlich gegenüber schnellen Spannungsanstiegen bzw. Überspannung und sollten nie ohne Vorwiderstand direkt an einer niederohmigen Quelle (Batterie) geladen/betrieben werden. Takos verbrennen im Fehlerfall mit einer Stichflamme, daher empfiehlt es sich eine deutlich höhere Spannungsfestigkeit zu wählen und - bei direktem Anschluß an eine niederohmige Quelle - zusätzlich ein Lade-Vorwiderstand einzubauen. Treten zusätzlich hohe Temperaturen auf, ist die Spannungsfestigkeit mindestens zu verdoppeln.&lt;br /&gt;
Kondensatoren mit Niob statt Tantal sind weniger verbreitet, und noch sehr teuer. Ihr Vorteil ist z.B. die Unempfindlichkeit gegenüber schnellen Spannungsanstiegen, sie können direkt an jeder Quelle betrieben werden.&lt;br /&gt;
&lt;br /&gt;
==== Flüssig-Elektrolyt ====&lt;br /&gt;
Diese Bauteile bestehen aus zwei Lagen &#039;&#039;&#039;sehr stark angeätzter&#039;&#039;&#039; Aluminiumfolie - zur Vergrößerung der Oberfläche - und einem flüssigen (seltener &amp;quot;einem festen&amp;quot;) Elektrolyten. Die zweite Folie kontaktiert gleichzeitig den Elektrolyten. Der Elektrolyt ist häufig auf Alkoholen basierend, aus/mit Schwefelsäure oder Glykol.&lt;br /&gt;
Die Alufolie wird nach den ätzen anodisch oxidiert. Die Eloxal-Schicht ist mechanisch belastbar, und stellt die &#039;&#039;&#039;einzige&#039;&#039;&#039; Isolation zwischen den beiden Anschlüssen dar. Die Oxidation der Alufolie wird je nach Hersteller bei ca. 140..165% der Nennspannung durchgeführt. Die Oxidschicht baut sich durch den Elektrolyten bei langer Lagerung stark ab, daher sinkt die Spannungsfestigkeit eines Elkos mit seinem Alter. Alte Bauteile können - sofern noch Elektrolyt enthalten ist - durch spannungsrichtiges Anschließen an eine Spannungsquelle und langsamen erhöhen der Spannung bis über die Nennspannung hinaus wieder &amp;quot;formiert&amp;quot; werden. Achtung, hier besteht die Gefahr eines Kurzschlusses zwischen den Anschlüssen, daher hochohmigen Vorwiderstand verwenden!&lt;br /&gt;
Wird ein Elko verpolt betrieben, wird die Oxidschicht im Laufe der Zeit (Abhängig von Spannung und Temperatur) abgebaut, und der Kondensator schlägt durch.&lt;br /&gt;
&lt;br /&gt;
=== Doppelschicht-Kondensatoren ===&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Superkondensator in [https://de.wikipedia.org/wiki/Superkondensator Wikipedia]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Diese Bauteile werden unter Markennamen wie &amp;quot;Goldcap&amp;quot;, &amp;quot;Ultracap&amp;quot; oder &amp;quot;Supercap&amp;quot; vertrieben, besitzen meist ein flüssiges Elektrolyt haben aber nicht besonders viel mit den o.g. &amp;quot;Elektrolytkondensatoren&amp;quot; gemeinsam. Dieser Kondensator besitzt Eigenschaften eines Kondensators und einer Batterie. Der &amp;quot;Kondensator&amp;quot; ist hier aus (im Gegensatz zum Elko) zwei Lagen einer einseitig metallisierten Aktivkohle, einem Separator, und einem Elektrolyten. &lt;br /&gt;
Das verwendete Elektrolyt erfüllt unter definierten Betriebsbedingungen die Funktion eines zusätzlichen Dielektrikums, und trägt zusätzlich zur ohnehin schon sehr großen Fläche der Aktivkohle, durch seine extrem dünne Schichtdicke, zur sehr hohen Kapazität bei.&lt;br /&gt;
Im Gegensatz zu einer Batterie kann der Doppelschichtkondensator Energie sehr schnell aufnehmen und abgeben, und altert dabei nur im geringen Maße. 500000 Lade-/Entladezyklen sind erreichbar, wenn die Spannung pro Zelle nicht überschritten wird. Normal sind hier 2,3..2,5V.&lt;br /&gt;
&lt;br /&gt;
== Anwendungen ==&lt;br /&gt;
Hier wird für verschiedene Anwendungsfälle eine sinnvolle Lösung empfohlen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Anwendung&#039;&#039;&#039;  || &#039;&#039;&#039;Beispiel&#039;&#039;&#039; || &#039;&#039;&#039;Empfohlener Typ&#039;&#039;&#039;  ||&#039;&#039;&#039;Kommentar&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Stromversorgung || Netzteil|| Elko||&lt;br /&gt;
|- &lt;br /&gt;
| Stromversorgung || Zwischenkreis|| Elkos, Folienkondensatoren||&lt;br /&gt;
|-&lt;br /&gt;
| Datenerhalt || || Doppelschichtkondensator ||&lt;br /&gt;
|-&lt;br /&gt;
| Energiespeicher|| digitale Schaltungen|| Kerko || z.B. 47nF pro IC bzw. Vcc-Anschluß&lt;br /&gt;
|- &lt;br /&gt;
| Hohe Impulsenergie x00ms || Impulsschweißgerät, (capacitive discharge)|| Elko (Folie)||&lt;br /&gt;
|-&lt;br /&gt;
| Impulsfeste Kondensatoren || Coils shrinker, Coilgun, Railgun|| Folie||MKT10 oder noch besser FKT1&lt;br /&gt;
|-&lt;br /&gt;
| NF-Filter|| PWM als DAC || Tako/Elko/Kerko ||&lt;br /&gt;
|-&lt;br /&gt;
| HF-Filter|| Beispiel || Styroflex-Folie ||&lt;br /&gt;
|-&lt;br /&gt;
| Audio || Kopplung || Folie ||&lt;br /&gt;
|-&lt;br /&gt;
| Vorwiderstand || Motorkondensator || MP oder Folie ||&lt;br /&gt;
|-&lt;br /&gt;
| Vorwiderstand || Kondensatornetzteil|| Kerko/Folie || Achtung, nur X2-Typen!&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Mathematik zum Kondensator ==&lt;br /&gt;
&lt;br /&gt;
Die Größe eines Kondensators ist seine Kapazität (Formelzeichen &#039;&#039;C&#039;&#039;), die als Ladung (Formelzeichen &#039;&#039;Q&#039;&#039;) durch Spannung (Formelzeichen &#039;&#039;U&#039;&#039;) definiert ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{Q}{U}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Einheit für die Kapazität ist Farad:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; [C]=F=\frac{As}{V}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reihenschaltung ===&lt;br /&gt;
&lt;br /&gt;
Für eine Reihenschaltung von &#039;&#039;n&#039;&#039; Kondensatoren gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{C_{ges}}=\frac{1}{C_1}+\frac{1}{C_2}+\dots+\frac{1}{C_n}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_{ges}=U_1+U_2+\dots+U_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I_{ges}=I_1=I_2=\dots=I_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parallelschaltung ===&lt;br /&gt;
&lt;br /&gt;
Für eine Parallelschaltung von &#039;&#039;n&#039;&#039; Kondensatoren gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_{ges}=C_1+C_2+\dots+C_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_{ges}=U_1=U_2=\dots=U_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I_{ges}=I_1+I_2+\dots+I_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Gespeicherte Energie ===&lt;br /&gt;
&lt;br /&gt;
Die in einem Kondensator gespeicherte Energie lässt sich durch die Formel&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; W=\frac{C}{2} \cdot U^2 = \frac{1}{2 \cdot C} \cdot Q^2 = \frac{U}{2} \cdot Q &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
berechnen.&lt;br /&gt;
&lt;br /&gt;
== Praxis ==&lt;br /&gt;
&lt;br /&gt;
=== Polaritätszeichen ===&lt;br /&gt;
&lt;br /&gt;
Elektrolytkondensatoren (kurz Elkos) sind in der Regel gepolt, d. h. Gleichspannungspegel müssen in einer vorgeschriebenen Polarität angelegt werden, damit das Dielektrikum nicht zerstört wird.  Bei Aluminium-Elkos wird dabei generell der Minuspol gekennzeichnet, bei kleinen Bauformen mit einem Strich, bei größeren Bauformen befinden sich u. U. auch noch Minuszeichen in diesem Strich eingebettet. Bei Tantal-Elkos hingegen wird immer der Pluspol gekennzeichnet, sowohl bei SMD als auch THT-Bauformen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:SMD-Elkos.jpeg]]&lt;br /&gt;
&lt;br /&gt;
Die gezeigten Elkos haben folgende Werte (von links nach rechts):&lt;br /&gt;
&lt;br /&gt;
* 220 µF, Spannungsfestigkeit 6 V&lt;br /&gt;
* 100 µF (&amp;lt;math&amp;gt;10 \cdot 10^7&amp;lt;/math&amp;gt; pF), 16 V&lt;br /&gt;
* 22 µF, 10 V&lt;br /&gt;
* 1 µF, 35 V&lt;br /&gt;
* 2,2 µF (&amp;lt;math&amp;gt;22 \cdot 10^5&amp;lt;/math&amp;gt; pF), 10 V&lt;br /&gt;
&lt;br /&gt;
Die beim 100-µF-Kondensator zu findende Buchstabenschreibweise für die Spannungsfestigkeit ist wenig gebräuchlich, aber gelegentlich anzutreffen.  Die Zuordnung ist:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| style=&amp;quot;width:8em&amp;quot; | &#039;&#039;&#039;Buchstabe&#039;&#039;&#039; ||  e  ||   g   ||   j   ||   A   ||   C   ||   D   ||   E   ||   V   ||   H   ||   J   ||   K&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039; || 2.5 || 4   || 6,3 ||  10 ||  16 ||  20 ||  25 || 35 || 50 || 63 || 80&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Spannungsfestigkeit kann auch durch eine zweistellige Kombination aus einer Ziffer und einem Buchstaben codiert sein:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| style=&amp;quot;width:8em&amp;quot; | &#039;&#039;&#039;Kombination&#039;&#039;&#039;  ||   0G  ||   0L  ||  0J   ||  1A   ||  1C   ||  1E &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  || 4,0  || 5,5  || 6,3  ||  10  ||  16  ||  20 &lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Kombination&#039;&#039;&#039;                      ||   1H  ||   1J  ||  1K   ||  2A   ||  2Q   ||  2B &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  ||  50  ||  63  ||  80  || 100  || 110  || 125 &lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Kombination&#039;&#039;&#039;                      ||   2C  ||   2Z  ||  2D   ||  2P   ||  2E   ||  2F&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  || 160  || 180  || 200  || 220  || 250  || 315 &lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Kombination&#039;&#039;&#039;                      ||   2V  ||   2G  ||  2W   ||  2H   ||  2J   ||  3A&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  || 350  || 400  || 450  || 500  || 630  || 1000 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Case Code&#039;&#039;&#039; (Bauform)      ||  A  ||  B  ||  C  ||  D   &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EIA Code&#039;&#039;&#039;                 ||  3216 || 3528  || 6032  ||  7343&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Länge&#039;&#039;&#039; (mm)               || 3,2 || 3,5 || 6,0 || 7,3 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Breite&#039;&#039;&#039; (mm)              || 1,6 || 2,8 || 3,2 || 4,3 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Höhe&#039;&#039;&#039; (mm)                || 1,6 || 1,9 || 2,5 || 2,8 &lt;br /&gt;
|-&lt;br /&gt;
|    Maßtoleranz (mm)            || 0,2 || 0,2 || 0,3 || 0,3  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Siebkondensator ===&lt;br /&gt;
&lt;br /&gt;
Der Siebkondensator sitzt hinter einem Gleichrichter und hat die Aufgabe, aus einer gleichgerichteten, pulsierenden Spannung, eine annähernd konstante Gleichspannung mit nur wenig Welligkeit (engl. Ripple) zu machen. Er wird periodisch geladen und muss während der Ladepausen, wenn die Eingangssspannung vor dem Gleichrichter kleiner als die Ausgangsspannung ist, den Ausgang mit Strom versorgen. Man findet ihn in allen klassischen Netzteilen mit 50Hz Trafo. Als Daumenregel kann man sich merken, dass man pro 1A Ausgangsstrom ca. 10mF braucht, um eine Welligkeit von ca. 1Vpp zu erreichen.&lt;br /&gt;
&lt;br /&gt;
Einen Siebkondensator findet man auch am Ausgang von Schaltnetzteilen, seine Aufgabe ist dort die gleiche. Allerdings sind die Schaltfrequenzen deutlich höher, typisch 50-500kHz. Darum muss dieser Kondensator einen besonders kleinen, effektiven Innenwiderstand besitzen (engl. ESR, Equivalent Series Resistance).&lt;br /&gt;
&lt;br /&gt;
=== Entkoppelkondensator ===&lt;br /&gt;
&lt;br /&gt;
Der Entkoppelkondensator hat die Aufgabe, die Versorgungsspannung nahe an einem IC für hochfrequente Ströme zu puffern (entkoppeln, engl. decoupling). Schnelle Digital- und Analogschaltungen benötigen vor allem beim Umschalten sehr schnell viel Strom, in der Größenordnung von Nanosekunden bis Mikrosekunden, je nach IC Milliampere bis Ampere. Diese müssen mit möglichst geringem Widerstand und Induktivität geliefert werden. Ein Stromversorgungsnetz auf einer Platine kann das meist nur unzureichend, dazu sind die Leitungen meist zu lang und damit die Induktivität zu hoch. Ein nah am IC platzierter Kondensator liefert diesen Strom für kurze Zeit, ohne dass die Spannung nennenswert einbricht. Die Entkopplung der Stromversorgung geschieht meist mehrstufig, d.h. es werden Kondensatoren verschiedener Arten und Kapazitäten eingesetzt, siehe [[Stromversorgung für FPGAs]].&lt;br /&gt;
&lt;br /&gt;
Praktische Anwendung&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Jeder&#039;&#039;&#039; Digitalschaltkreis benötigt einen 100nF Keramikkondensator nah (kleiner 20mm) an den Anschlüssen von VCC und GND. Je schneller der IC schalten kann, umso wichtiger ist er und umso näher muss er platziert werden. Ein &amp;quot;langsamer&amp;quot; IC wie ein AVR Mikrocontroller verkraftet 20mm, im Ausnahmefall auch mehr. Ein deutlich schnellerer IC wie moderne FPGAs, große CPUs, sehr schnelle ASICs etc. sind da anspruchsvoller, vor allem wenn die Schaltung stabil laufen soll. Dann muss man möglichst jeden Millimeter Leiterbahn zwischen Pin und Kondensator einsparen, soweit das mechanisch geht.&lt;br /&gt;
* Für &#039;&#039;&#039;jedes&#039;&#039;&#039; Anschlusspaar von VCC und GND eines ICs muss ein Kondensator verwendet werden. Sparen geht hier oft schief!&lt;br /&gt;
* Für die Verbindung der Entkoppelkondensatoren zur Masse- bzw. Versorgungsfläche sollte man möglichst je Kondensator ein VIA benutzen und nicht über ein VIA mehrere Kondensatoren verbinden. Dadurch wird die parasitäre Induktivität vermindert.&lt;br /&gt;
* Schnelle Analogschaltkreise wie Operationsverstärker, [[Treiber]] etc. brauchen auch individuelle Entkoppelkondensatoren.&lt;br /&gt;
* Pi mal Daumen gilt: Je größer der Kondensator, umso weiter kann er von dem Verbraucher weg sein, da er auf Grund seines Innenwiderstands (ESR, ESL) weniger HF-tauglich ist. Es ist somit nicht sinnvoll, einen 1000µF Elektrolytkondensator 10mm neben einen Digital-IC setzen zu wollen. Dort gehört der 100nF Keramikkondensator hin. Aber für die Stromversorgung von Motoren, Treibern und [[H-Brücken Übersicht|H-Brücken]] sind derartige großen Kondensatoren wichtig und sollten nicht zu weit entfernt sein.&lt;br /&gt;
&lt;br /&gt;
=== Koppelkondensator ===&lt;br /&gt;
&lt;br /&gt;
Koppelkondensatoren verbinden Verstärkerstufen. Dabei wird jedoch nur der Wechselanteil übertragen, kein Gleichanteil. Diese Kondensatoren müssen möglichst verzerrungsarm sein, vor allem im Audiobereich. Das wird durch die richtige Wahl des Dielektrikums erreicht.&lt;br /&gt;
&lt;br /&gt;
=== Forumsbeiträge ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/43762 Abblockkondensator: Kerko (Keramik) oder Folie?]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/62576 Brennende Tantalkondensatoren]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/108865#963281 Kodierung (Umwandlung)]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/169958#1624840 Unterschied: Elektrolytkondensator vs. Tantalkondensator]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/195563#1915294 Unterschied: C0G vs. Glimmer]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/264347#2748845 Kurze Beschreibung verschiedener Folienkondensatoren]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/108865#1356075 PC Tool zum Identifizieren von Kondensatoren]&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/436472#5164488 Kräftige Ladepumpe 12V?], wie Ladungspumpen effizient arbeiten&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/470224#5763932 MMLC Kondensatoren Bauform reduzieren]&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/440901#5264220 1F-Kondensator laden &amp;amp; entladen]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://my.execpc.com/~endlr/index.html CapSite 2009] - Introduction To Capacitors&lt;br /&gt;
* [http://www.cde.com/catalogs/AEappGUIDE.pdf Aluminum Electrolytic Capacitor Application Guide] von CDE Cornell Dubilier&lt;br /&gt;
* Sehr ausf&amp;amp;uuml;hrliche Artikel bei Wikipedia&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Elektrolytkondensator Elektrolytkondensator]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Polymer-Elektrolytkondensator Polymer-Elkos]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Aluminium-Elektrolytkondensator Al-(Polymer-)Elkos]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Tantal-Elektrolytkondensator Niob- und Tantal-(Polymer-)Elkos]&lt;br /&gt;
* Anwendungen&lt;br /&gt;
** [http://www.radio-electronics.com/info/data/capacitor/capacitor_types.php Capacitor types and their uses] (engl.)&lt;br /&gt;
** [http://www.planetanalog.com/features/showArticle.jhtml;jsessionid=23?articleID=199905522 Choosing and Using Bypass Capacitors] (dreiteilige Artikelserie) bei [http://www.planetanalog.com www.planetanalog.com]&lt;br /&gt;
** [http://www.vagrearg.org/?p=decoupling Decoupling by Example]&lt;br /&gt;
* Video-Tutorials&lt;br /&gt;
** EEVblog #33 – Capacitor Tutorial [http://www.eevblog.com/2009/09/26/eevblog-33-1of2-capacitor-tutorial-electrolytic-tantalum-and-plastic-film/ Teil 1: Electrolytic, Tantalum, and Plastic Film] und [http://www.eevblog.com/2009/09/26/eevblog-33-2of2-capacitor-tutorial-ceramics-and-impedance/ Teil 2: Ceramics and impedance]&lt;br /&gt;
* [http://www.maximintegrated.com/app-notes/index.mvp/id/5527 Tutorial] von Maxim, &amp;quot;Temperature and Voltage Variation of Ceramic Capacitors, or Why Your 4.7µF Capacitor Becomes a 0.33µF Capacitor&amp;quot;, engl.&lt;br /&gt;
* [http://www.wima.de/DE/characteristics.htm Vergleichstabelle] der Eigenschaften von Folien- und Keramikkondensatoren, WIMA&lt;br /&gt;
* [http://portal.national.com/rap/Application/0,1570,28,00.html] Bob Pease: &amp;quot;Understand Capacitor Soakage to Optimize Analog Systems&amp;quot;&lt;br /&gt;
* [http://www.youtube.com/watch?v=AP_FSyWGpoE Youtube-Video zum Thema Abblockkondensatoren (Deutsch)]&lt;br /&gt;
* [http://www.lothar-miller.de/s9y/categories/14-Entkopplung Entkopplung] von ICs, von Lothar Miller&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Kondensator&amp;diff=107594</id>
		<title>Kondensator</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Kondensator&amp;diff=107594"/>
		<updated>2025-08-04T08:14:45Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* Keramik-Kondensator */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein &#039;&#039;&#039;Kondensator&#039;&#039;&#039; ist ein passives elektrisches Bauteil mit zwei Anschlüssen, welches elektrische Energie in einem elektrischen Feld in Form von Ladung speichert. Die charakteristische Größe, die den Kondensator beschreibt, ist die Kapazität C, gemessen in Farad (F). Ein Kondensator besteht aus zwei Flächen gut leitfähigen Materials, die voneinander isoliert sind. Das Isolationsmaterial ist ein Dielektrikum und verleiht dem Kondensator je nach Material stark unterschiedliche Eigenschaften.&lt;br /&gt;
&lt;br /&gt;
== Aufbau eines Kondensators ==&lt;br /&gt;
Je größer die Fläche, je geringer der Abstand der Flächen und je höher die relative Permittivität \varepsilon_r - das Dielektrikum - desto höher ist die Kapazität des Bauteils. Der einfachste Kondensator besteht aus zwei voneinander isolierten Metallplatten, zwischen denen sich Luft als &amp;quot;Dielektrikum&amp;quot; befindet. Um die Fläche und damit die Kapazität zu vergrößern gibt es verschiedene Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
=== Wickelkondensator ===&lt;br /&gt;
Die einfachste Methode die Flächen zu vergrößern ist es, zwei leitfähige Folien mit einem Isolationsmaterial dazwischen aufzuwickeln. Diese Methode wird für so ziemlich alle Kondensatortypen verwendet. Manchmal wird der Wickel nach der Herstellung flachgepresst, um das Volumen zu optimieren.&lt;br /&gt;
&lt;br /&gt;
=== Schichtkondensator ===&lt;br /&gt;
Diese Aufbauart wird durch &amp;quot;stapeln&amp;quot; der unterschiedlichen Lagen erreicht. Verfahrenstechnisch ist dies oft wieder ein Wickel, jedoch meist auf bis zu 2 Meter durchmessenden Wickelrädern. Dieses ursprünglich von Siemens eingesetzte Verfahren wird auch aktuell noch verwendet, hauptsächlich für Folienkondensatoren bis in den mF-Bereich hinein. Nach dem Wickeln auf vieleckigen Rädern wird der Folienstapel geschnitten und auf die gewünschte Größe = Kapazität getrennt.&lt;br /&gt;
&lt;br /&gt;
== Kondensatortypen ==&lt;br /&gt;
(Vakuum-, Glimmer-, Gas- und Glaskondensatoren nicht berücksichtigt)&lt;br /&gt;
=== Keramik-Kondensator ===&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Keramikkondensator in [https://de.wikipedia.org/wiki/Keramikkondensator#Spannungsabh.C3.A4ngigkeit_der_Kapazit.C3.A4t Wikipedia]&#039;&#039;&#039;. Es gibt sie bedrahtet und in SMD. Bedrahtet gab es sie früher als Einschicht-Kapazität in Scheibenform, heutzutage nur noch als Vielschicht-Kondensator. Einschichtig nur für Spannungen im Kilovolt-Bereich.&lt;br /&gt;
&lt;br /&gt;
Kerkos sind sogenannte &amp;quot;Vielschicht-Kondensatoren&amp;quot; die aus mehreren hundert Lagen einer isolierenden Keramik (Titandioxid+Bariumtitanat), und einer elektrisch leitfähigen Metallisierung (Aluminium &amp;amp; Magnesiumsilikate) bestehen.&lt;br /&gt;
Dieser Typ ist wie der Folienkondensator für höchste Frequenzen geeignet, hat eine sehr geringe Baugröße, eine gute Temperaturstabilität aber piezoelektrische Eigenschaften. Die Kapazität pro mm³ variiert je nach Dielektrikum Z5U, Y5V, X7R, C0 (in abnehmender Kapazitätsdichte) und ist sowohl abhängig von Temperatur als auch der angelegten Spannung.&lt;br /&gt;
&lt;br /&gt;
Keramik-Kondensatoren sind sehr preisgünstig und werden überall dort eingesetzt, wo andere Kondensatortypen keinen Vorteil bringen. Zudem lösen sie mehr und mehr Elektrolytkondensatoren ab, die ihrerseits mit hoher Kapazität punkten: Die Lebensdauer, besonders bei hoher Temperatur, ist höher.&lt;br /&gt;
&lt;br /&gt;
=== Folien-Kondensator ===&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Folienkondensator in [https://de.wikipedia.org/wiki/Kunststoff-Folienkondensator Wikipedia]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Unterschiedliche Folienarten verleihen dem Kondensator stark unterschiedliche Eigenschaften wie z.B. temperatur- und alterungsbeständigkeit, Verlustfaktor, Isolationswiderstand, und die obere Grenzfrequenz, um nur die wichtigsten zu nennen. Die Dicke der Metallisierung hingegen entscheidet über den maximal zulässigen Rippelstrom / Impulsstromfestigkeit und darüber, ob sich ein Kondensator &amp;quot;selbst heilen&amp;quot; kann, oder nicht. Das Verfahren der &amp;quot;Selbstheilung&amp;quot; funktioniert nur bei Metallisierungen, da es darauf beruht, daß die im C gespeicherte Energie den Metallbelag beim Durchschlag durch die Isolation verdampfen kann. Ist die Energie zu gering, wird nur wenig Metall um den Durchschlag verdampft, und der Kurzschluß bleibt bestehen. Ist die Energie zu hoch, wird der Kondensator thermisch zerstört. Hat der C den richigen Energiegehalt, verdampft direkt um die Durchschlagstelle herum bis zu einigen mm der Metallisierung, und die Fehlstelle ist wieder isoliert. Im Übrigen ist eine Parallelsschaltung aus vielen,als selbstheilenden C&#039;s nicht unbedingt mehr &amp;quot;selbstheilend&amp;quot; da der Energieeintrag dann aus dem Gesamtverbund kommt, und oft zu hoch ist um nur eine kleine Fläche verdampfen zu lassen.&lt;br /&gt;
Impulsstrom-feste Kondensatoren zeichnen sich durch hohe Schichtstärken der Metallisierung aus, oder verwenden statt der Metallbeschichtung direkt eine Metallfolie. Hier wäre normalerweise ein &amp;quot;selbstheilen&amp;quot; gleichbedeutend mit einem so hohen Energieeintrag, daß das Bauteil zerstört wird.&lt;br /&gt;
&lt;br /&gt;
Hier eine kleine Übersicht mit der Bitte um Vervollständigung.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| style=&amp;quot;width:8em&amp;quot;| &#039;&#039;&#039;Folientyp&#039;&#039;&#039;  || Poly-&amp;lt;br&amp;gt;propylen || Poly-&amp;lt;br&amp;gt;styren || Poly-&amp;lt;br&amp;gt;carbonat  ||Poly-&amp;lt;br&amp;gt;ester|| Poly-&amp;lt;br&amp;gt;styrol  ||   Poly-&amp;lt;br&amp;gt;ethylen-&amp;lt;br&amp;gt;naphthalat ||   Polytetra-&amp;lt;br&amp;gt;fluor-&amp;lt;br&amp;gt;ethylen ||   Poly-&amp;lt;br&amp;gt;phenylen-&amp;lt;br&amp;gt;sulfid    ||   Metall-&amp;lt;br&amp;gt;Papier   &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Folien-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;markenname&#039;&#039;&#039; || Hostalen || -   || Makrofol, Makroflex ||  Mylar, Hostaphan ||   Styroflex ||  Kaladex||  Teflon|| Tedur, Ryton|| Papier&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Abkürzung&#039;&#039;&#039; || PP || -  || PC||  PET ||  PS||  PEN||  PTFE|| PPS|| -  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Typ&#039;&#039;&#039; || (F)KP, MKP || -KS, MKS   || KC, MKC ||  (F)KT, MKT  ||  -KS, MKS ||  (F)KN, MKN||  -|| (F)KI, MKI|| MP  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Temperatur-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /°C&#039;&#039;&#039; || ca 105 || -    || ca. 105 ||  125..150  ||  -  ||  150 ||  -|| 150 || 85  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Toleranz&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;+/-%&#039;&#039;&#039; || 1 || 1   || -||  1  ||  -  ||  -||  -|| -|| 20  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Grenzfrequenz /kHz&#039;&#039;&#039; || 100 || 400   || -||  -  ||  1000  ||  -||  -|| -|| -  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Selbstheilend&#039;&#039;&#039; || ja || -  || -||  ja  ||  -  ||  -||  -|| -|| ja  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Impuls-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;belastbarkeit&#039;&#039;&#039; || + || +   || ||  +  ||  -- ||  -||  -|| -|| +  &lt;br /&gt;
|}&lt;br /&gt;
(MK) = metallisierte Folie,  (F) = Metallbeläge z.B. Metallfolie (K) = Metallfolie+Kunststofffolie&lt;br /&gt;
&lt;br /&gt;
=== Elektrolyt-Kondensator ===&lt;br /&gt;
Ein Elko unterscheidet sich hauptsächlich dadurch von anderen Kondensatoren, daß nur eine Elektrode aus einer Metallschicht besteht, die zweite aus einem festen, oder flüssigen Elektrolyten.&lt;br /&gt;
&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Elektrolytkondensator in [https://de.wikipedia.org/wiki/Elektrolytkondensator Wikipedia]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Fest-Elektrolyt ====&lt;br /&gt;
Diese Bauteile bestehen aus Polymeren, Metalloxiden, Braunstein, ... und sind wie die Elkos immer gepolt.&lt;br /&gt;
Als Metalloxide ist Tantal sehr, und Niob noch weniger verbreitet.&lt;br /&gt;
Takos sind &#039;&#039;&#039;SEHR&#039;&#039;&#039; empfindlich gegenüber schnellen Spannungsanstiegen bzw. Überspannung und sollten nie ohne Vorwiderstand direkt an einer niederohmigen Quelle (Batterie) geladen/betrieben werden. Takos verbrennen im Fehlerfall mit einer Stichflamme, daher empfiehlt es sich eine deutlich höhere Spannungsfestigkeit zu wählen und - bei direktem Anschluß an eine niederohmige Quelle - zusätzlich ein Lade-Vorwiderstand einzubauen. Treten zusätzlich hohe Temperaturen auf, ist die Spannungsfestigkeit mindestens zu verdoppeln.&lt;br /&gt;
Kondensatoren mit Niob statt Tantal sind weniger verbreitet, und noch sehr teuer. Ihr Vorteil ist z.B. die Unempfindlichkeit gegenüber schnellen Spannungsanstiegen, sie können direkt an jeder Quelle betrieben werden.&lt;br /&gt;
&lt;br /&gt;
==== Flüssig-Elektrolyt ====&lt;br /&gt;
Diese Bauteile bestehen aus zwei Lagen &#039;&#039;&#039;sehr stark angeätzter&#039;&#039;&#039; Aluminiumfolie - zur Vergrößerung der Oberfläche - und einem flüssigen (seltener &amp;quot;einem festen&amp;quot;) Elektrolyten. Die zweite Folie kontaktiert gleichzeitig den Elektrolyten. Der Elektrolyt ist häufig auf Alkoholen basierend, aus/mit Schwefelsäure oder Glykol.&lt;br /&gt;
Die Alufolie wird nach den ätzen anodisch oxidiert. Die Eloxal-Schicht ist mechanisch belastbar, und stellt die &#039;&#039;&#039;einzige&#039;&#039;&#039; Isolation zwischen den beiden Anschlüssen dar. Die Oxidation der Alufolie wird je nach Hersteller bei ca. 140..165% der Nennspannung durchgeführt. Die Oxidschicht baut sich durch den Elektrolyten bei langer Lagerung stark ab, daher sinkt die Spannungsfestigkeit eines Elkos mit seinem Alter. Alte Bauteile können - sofern noch Elektrolyt enthalten ist - durch spannungsrichtiges Anschließen an eine Spannungsquelle und langsamen erhöhen der Spannung bis über die Nennspannung hinaus wieder &amp;quot;formiert&amp;quot; werden. Achtung, hier besteht die Gefahr eines Kurzschlusses zwischen den Anschlüssen, daher hochohmigen Vorwiderstand verwenden!&lt;br /&gt;
Wird ein Elko verpolt betrieben, wird die Oxidschicht im Laufe der Zeit (Abhängig von Spannung und Temperatur) abgebaut, und der Kondensator schlägt durch.&lt;br /&gt;
&lt;br /&gt;
=== Doppelschicht-Kondensatoren ===&lt;br /&gt;
Siehe auch den Artikel &#039;&#039;&#039;Superkondensator in [https://de.wikipedia.org/wiki/Superkondensator Wikipedia]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Diese Bauteile werden unter Markennamen wie &amp;quot;Goldcap&amp;quot;, &amp;quot;Ultracap&amp;quot; oder &amp;quot;Supercap&amp;quot; vertrieben, besitzen meist ein flüssiges Elektrolyt haben aber nicht besonders viel mit den o.g. &amp;quot;Elektrolytkondensatoren&amp;quot; gemeinsam. Dieser Kondensator besitzt Eigenschaften eines Kondensators und einer Batterie. Der &amp;quot;Kondensator&amp;quot; ist hier aus (im Gegensatz zum Elko) zwei Lagen einer einseitig metallisierten Aktivkohle, einem Separator, und einem Elektrolyten. &lt;br /&gt;
Das verwendete Elektrolyt erfüllt unter definierten Betriebsbedingungen die Funktion eines zusätzlichen Dielektrikums, und trägt zusätzlich zur ohnehin schon sehr großen Fläche der Aktivkohle, durch seine extrem dünne Schichtdicke, zur sehr hohen Kapazität bei.&lt;br /&gt;
Im Gegensatz zu einer Batterie kann der Doppelschichtkondensator Energie sehr schnell aufnehmen und abgeben, und altert dabei nur im geringen Maße. 500000 Lade-/Entladezyklen sind erreichbar, wenn die Spannung pro Zelle nicht überschritten wird. Normal sind hier 2,3..2,5V.&lt;br /&gt;
&lt;br /&gt;
== Anwendungen ==&lt;br /&gt;
Hier wird für verschiedene Anwendungsfälle eine sinnvolle Lösung empfohlen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Anwendung&#039;&#039;&#039;  || &#039;&#039;&#039;Beispiel&#039;&#039;&#039; || &#039;&#039;&#039;Empfohlener Typ&#039;&#039;&#039;  ||&#039;&#039;&#039;Kommentar&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Stromversorgung || Netzteil|| Elko||&lt;br /&gt;
|- &lt;br /&gt;
| Stromversorgung || Zwischenkreis|| Elkos, Folienkondensatoren||&lt;br /&gt;
|-&lt;br /&gt;
| Datenerhalt || || Doppelschichtkondensator ||&lt;br /&gt;
|-&lt;br /&gt;
| Energiespeicher|| digitale Schaltungen|| Kerko || z.B. 47nF pro IC bzw. Vcc-Anschluß&lt;br /&gt;
|- &lt;br /&gt;
| Hohe Impulsenergie x00ms || Impulsschweißgerät, (capacitive discharge)|| Elko (Folie)||&lt;br /&gt;
|-&lt;br /&gt;
| Impulsfeste Kondensatoren || Coils shrinker, Coilgun, Railgun|| Folie||MKT10 oder noch besser FKT1&lt;br /&gt;
|-&lt;br /&gt;
| NF-Filter|| PWM als DAC || Tako/Elko/Kerko ||&lt;br /&gt;
|-&lt;br /&gt;
| HF-Filter|| Beispiel || Styroflex-Folie ||&lt;br /&gt;
|-&lt;br /&gt;
| Audio || Kopplung || Folie ||&lt;br /&gt;
|-&lt;br /&gt;
| Vorwiderstand || Motorkondensator || MP oder Folie ||&lt;br /&gt;
|-&lt;br /&gt;
| Vorwiderstand || Kondensatornetzteil|| Kerko/Folie || Achtung, nur X2-Typen!&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Mathematik zum Kondensator ==&lt;br /&gt;
&lt;br /&gt;
Die Größe eines Kondensators ist seine Kapazität (Formelzeichen &#039;&#039;C&#039;&#039;), die als Ladung (Formelzeichen &#039;&#039;Q&#039;&#039;) durch Spannung (Formelzeichen &#039;&#039;U&#039;&#039;) definiert ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{Q}{U}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Einheit für die Kapazität ist Farad:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; [C]=F=\frac{As}{V}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reihenschaltung ===&lt;br /&gt;
&lt;br /&gt;
Für eine Reihenschaltung von &#039;&#039;n&#039;&#039; Kondensatoren gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{C_{ges}}=\frac{1}{C_1}+\frac{1}{C_2}+\dots+\frac{1}{C_n}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_{ges}=U_1+U_2+\dots+U_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I_{ges}=I_1=I_2=\dots=I_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parallelschaltung ===&lt;br /&gt;
&lt;br /&gt;
Für eine Parallelschaltung von &#039;&#039;n&#039;&#039; Kondensatoren gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_{ges}=C_1+C_2+\dots+C_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_{ges}=U_1=U_2=\dots=U_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I_{ges}=I_1+I_2+\dots+I_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Gespeicherte Energie ===&lt;br /&gt;
&lt;br /&gt;
Die in einem Kondensator gespeicherte Energie lässt sich durch die Formel&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; W=\frac{C}{2} \cdot U^2 = \frac{1}{2 \cdot C} \cdot Q^2 = \frac{U}{2} \cdot Q &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
berechnen.&lt;br /&gt;
&lt;br /&gt;
== Praxis ==&lt;br /&gt;
&lt;br /&gt;
=== Polaritätszeichen ===&lt;br /&gt;
&lt;br /&gt;
Elektrolytkondensatoren (kurz Elkos) sind in der Regel gepolt, d. h. Gleichspannungspegel müssen in einer vorgeschriebenen Polarität angelegt werden, damit das Dielektrikum nicht zerstört wird.  Bei Aluminium-Elkos wird dabei generell der Minuspol gekennzeichnet, bei kleinen Bauformen mit einem Strich, bei größeren Bauformen befinden sich u. U. auch noch Minuszeichen in diesem Strich eingebettet. Bei Tantal-Elkos hingegen wird immer der Pluspol gekennzeichnet, sowohl bei SMD als auch THT-Bauformen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:SMD-Elkos.jpeg]]&lt;br /&gt;
&lt;br /&gt;
Die gezeigten Elkos haben folgende Werte (von links nach rechts):&lt;br /&gt;
&lt;br /&gt;
* 220 µF, Spannungsfestigkeit 6 V&lt;br /&gt;
* 100 µF (&amp;lt;math&amp;gt;10 \cdot 10^7&amp;lt;/math&amp;gt; pF), 16 V&lt;br /&gt;
* 22 µF, 10 V&lt;br /&gt;
* 1 µF, 35 V&lt;br /&gt;
* 2,2 µF (&amp;lt;math&amp;gt;22 \cdot 10^5&amp;lt;/math&amp;gt; pF), 10 V&lt;br /&gt;
&lt;br /&gt;
Die beim 100-µF-Kondensator zu findende Buchstabenschreibweise für die Spannungsfestigkeit ist wenig gebräuchlich, aber gelegentlich anzutreffen.  Die Zuordnung ist:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| style=&amp;quot;width:8em&amp;quot; | &#039;&#039;&#039;Buchstabe&#039;&#039;&#039; ||  e  ||   g   ||   j   ||   A   ||   C   ||   D   ||   E   ||   V   ||   H   ||   J   ||   K&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039; || 2.5 || 4   || 6,3 ||  10 ||  16 ||  20 ||  25 || 35 || 50 || 63 || 80&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Spannungsfestigkeit kann auch durch eine zweistellige Kombination aus einer Ziffer und einem Buchstaben codiert sein:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| style=&amp;quot;width:8em&amp;quot; | &#039;&#039;&#039;Kombination&#039;&#039;&#039;  ||   0G  ||   0L  ||  0J   ||  1A   ||  1C   ||  1E &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  || 4,0  || 5,5  || 6,3  ||  10  ||  16  ||  20 &lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Kombination&#039;&#039;&#039;                      ||   1H  ||   1J  ||  1K   ||  2A   ||  2Q   ||  2B &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  ||  50  ||  63  ||  80  || 100  || 110  || 125 &lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Kombination&#039;&#039;&#039;                      ||   2C  ||   2Z  ||  2D   ||  2P   ||  2E   ||  2F&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  || 160  || 180  || 200  || 220  || 250  || 315 &lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Kombination&#039;&#039;&#039;                      ||   2V  ||   2G  ||  2W   ||  2H   ||  2J   ||  3A&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Spannungs-&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;festigkeit /V&#039;&#039;&#039;  || 350  || 400  || 450  || 500  || 630  || 1000 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Case Code&#039;&#039;&#039; (Bauform)      ||  A  ||  B  ||  C  ||  D   &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EIA Code&#039;&#039;&#039;                 ||  3216 || 3528  || 6032  ||  7343&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Länge&#039;&#039;&#039; (mm)               || 3,2 || 3,5 || 6,0 || 7,3 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Breite&#039;&#039;&#039; (mm)              || 1,6 || 2,8 || 3,2 || 4,3 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Höhe&#039;&#039;&#039; (mm)                || 1,6 || 1,9 || 2,5 || 2,8 &lt;br /&gt;
|-&lt;br /&gt;
|    Maßtoleranz (mm)            || 0,2 || 0,2 || 0,3 || 0,3  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Siebkondensator ===&lt;br /&gt;
&lt;br /&gt;
Der Siebkondensator sitzt hinter einem Gleichrichter und hat die Aufgabe, aus einer gleichgerichteten, pulsierenden Spannung, eine annähernd konstante Gleichspannung mit nur wenig Welligkeit (engl. Ripple) zu machen. Er wird periodisch geladen und muss während der Ladepausen, wenn die Eingangssspannung vor dem Gleichrichter kleiner als die Ausgangsspannung ist, den Ausgang mit Strom versorgen. Man findet ihn in allen klassischen Netzteilen mit 50Hz Trafo. Als Daumenregel kann man sich merken, dass man pro 1A Ausgangsstrom ca. 10mF braucht, um eine Welligkeit von ca. 1Vpp zu erreichen.&lt;br /&gt;
&lt;br /&gt;
Einen Siebkondensator findet man auch am Ausgang von Schaltnetzteilen, seine Aufgabe ist dort die gleiche. Allerdings sind die Schaltfrequenzen deutlich höher, typisch 50-500kHz. Darum muss dieser Kondensator einen besonders kleinen, effektiven Innenwiderstand besitzen (engl. ESR, Equivalent Series Resistance).&lt;br /&gt;
&lt;br /&gt;
=== Entkoppelkondensator ===&lt;br /&gt;
&lt;br /&gt;
Der Entkoppelkondensator hat die Aufgabe, die Versorgungsspannung nahe an einem IC für hochfrequente Ströme zu puffern (entkoppeln, engl. decoupling). Schnelle Digital- und Analogschaltungen benötigen vor allem beim Umschalten sehr schnell viel Strom, in der Größenordnung von Nanosekunden bis Mikrosekunden, je nach IC Milliampere bis Ampere. Diese müssen mit möglichst geringem Widerstand und Induktivität geliefert werden. Ein Stromversorgungsnetz auf einer Platine kann das meist nur unzureichend, dazu sind die Leitungen meist zu lang und damit die Induktivität zu hoch. Ein nah am IC platzierter Kondensator liefert diesen Strom für kurze Zeit, ohne dass die Spannung nennenswert einbricht. Die Entkopplung der Stromversorgung geschieht meist mehrstufig, d.h. es werden Kondensatoren verschiedener Arten und Kapazitäten eingesetzt, siehe [[Stromversorgung für FPGAs]].&lt;br /&gt;
&lt;br /&gt;
Praktische Anwendung&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Jeder&#039;&#039;&#039; Digitalschaltkreis benötigt einen 100nF Keramikkondensator nah (kleiner 20mm) an den Anschlüssen von VCC und GND. Je schneller der IC schalten kann, umso wichtiger ist er und umso näher muss er platziert werden. Ein &amp;quot;langsamer&amp;quot; IC wie ein AVR Mikrocontroller verkraftet 20mm, im Ausnahmefall auch mehr. Ein deutlich schnellerer IC wie moderne FPGAs, große CPUs, sehr schnelle ASICs etc. sind da anspruchsvoller, vor allem wenn die Schaltung stabil laufen soll. Dann muss man möglichst jeden Millimeter Leiterbahn zwischen Pin und Kondensator einsparen, soweit das mechanisch geht.&lt;br /&gt;
* Für &#039;&#039;&#039;jedes&#039;&#039;&#039; Anschlusspaar von VCC und GND eines ICs muss ein Kondensator verwendet werden. Sparen geht hier oft schief!&lt;br /&gt;
* Für die Verbindung der Entkoppelkondensatoren zur Masse- bzw. Versorgungsfläche sollte man möglichst je Kondensator ein VIA benutzen und nicht über ein VIA mehrere Kondensatoren verbinden. Dadurch wird die parasitäre Induktivität vermindert.&lt;br /&gt;
* Schnelle Analogschaltkreise wie Operationsverstärker, [[Treiber]] etc. brauchen auch individuelle Entkoppelkondensatoren.&lt;br /&gt;
* Pi mal Daumen gilt: Je größer der Kondensator, umso weiter kann er von dem Verbraucher weg sein, da er auf Grund seines Innenwiderstands (ESR, ESL) weniger HF-tauglich ist. Es ist somit nicht sinnvoll, einen 1000µF Elektrolytkondensator 10mm neben einen Digital-IC setzen zu wollen. Dort gehört der 100nF Keramikkondensator hin. Aber für die Stromversorgung von Motoren, Treibern und [[H-Brücken Übersicht|H-Brücken]] sind derartige großen Kondensatoren wichtig und sollten nicht zu weit entfernt sein.&lt;br /&gt;
&lt;br /&gt;
=== Koppelkondensator ===&lt;br /&gt;
&lt;br /&gt;
Koppelkondensatoren verbinden Verstärkerstufen. Dabei wird jedoch nur der Wechselanteil übertragen, kein Gleichanteil. Diese Kondensatoren müssen möglichst verzerrungsarm sein, vor allem im Audiobereich. Das wird durch die richtige Wahl des Dielektrikums erreicht.&lt;br /&gt;
&lt;br /&gt;
=== Forumsbeiträge ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/43762 Abblockkondensator: Kerko (Keramik) oder Folie?]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/62576 Brennende Tantalkondensatoren]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/108865#963281 Kodierung (Umwandlung)]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/169958#1624840 Unterschied: Elektrolytkondensator vs. Tantalkondensator]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/195563#1915294 Unterschied: C0G vs. Glimmer]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/264347#2748845 Kurze Beschreibung verschiedener Folienkondensatoren]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/108865#1356075 PC Tool zum Identifizieren von Kondensatoren]&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/436472#5164488 Kräftige Ladepumpe 12V?], wie Ladungspumpen effizient arbeiten&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/470224#5763932 MMLC Kondensatoren Bauform reduzieren]&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/440901#5264220 1F-Kondensator laden &amp;amp; entladen]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://my.execpc.com/~endlr/index.html CapSite 2009] - Introduction To Capacitors&lt;br /&gt;
* [http://www.cde.com/catalogs/AEappGUIDE.pdf Aluminum Electrolytic Capacitor Application Guide] von CDE Cornell Dubilier&lt;br /&gt;
* Sehr ausf&amp;amp;uuml;hrliche Artikel bei Wikipedia&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Elektrolytkondensator Elektrolytkondensator]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Polymer-Elektrolytkondensator Polymer-Elkos]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Aluminium-Elektrolytkondensator Al-(Polymer-)Elkos]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Tantal-Elektrolytkondensator Niob- und Tantal-(Polymer-)Elkos]&lt;br /&gt;
* Anwendungen&lt;br /&gt;
** [http://www.radio-electronics.com/info/data/capacitor/capacitor_types.php Capacitor types and their uses] (engl.)&lt;br /&gt;
** [http://www.planetanalog.com/features/showArticle.jhtml;jsessionid=23?articleID=199905522 Choosing and Using Bypass Capacitors] (dreiteilige Artikelserie) bei [http://www.planetanalog.com www.planetanalog.com]&lt;br /&gt;
** [http://www.vagrearg.org/?p=decoupling Decoupling by Example]&lt;br /&gt;
* Video-Tutorials&lt;br /&gt;
** EEVblog #33 – Capacitor Tutorial [http://www.eevblog.com/2009/09/26/eevblog-33-1of2-capacitor-tutorial-electrolytic-tantalum-and-plastic-film/ Teil 1: Electrolytic, Tantalum, and Plastic Film] und [http://www.eevblog.com/2009/09/26/eevblog-33-2of2-capacitor-tutorial-ceramics-and-impedance/ Teil 2: Ceramics and impedance]&lt;br /&gt;
* [http://www.maximintegrated.com/app-notes/index.mvp/id/5527 Tutorial] von Maxim, &amp;quot;Temperature and Voltage Variation of Ceramic Capacitors, or Why Your 4.7µF Capacitor Becomes a 0.33µF Capacitor&amp;quot;, engl.&lt;br /&gt;
* [http://www.wima.de/DE/characteristics.htm Vergleichstabelle] der Eigenschaften von Folien- und Keramikkondensatoren, WIMA&lt;br /&gt;
* [http://portal.national.com/rap/Application/0,1570,28,00.html] Bob Pease: &amp;quot;Understand Capacitor Soakage to Optimize Analog Systems&amp;quot;&lt;br /&gt;
* [http://www.youtube.com/watch?v=AP_FSyWGpoE Youtube-Video zum Thema Abblockkondensatoren (Deutsch)]&lt;br /&gt;
* [http://www.lothar-miller.de/s9y/categories/14-Entkopplung Entkopplung] von ICs, von Lothar Miller&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=MOSFET-%C3%9Cbersicht&amp;diff=107573</id>
		<title>MOSFET-Übersicht</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=MOSFET-%C3%9Cbersicht&amp;diff=107573"/>
		<updated>2025-06-26T09:31:43Z</updated>

		<summary type="html">&lt;p&gt;Heha: Beim nächsten Mal R-Spalte in Ohm!&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Im Forum wird immer wieder gefragt, welchen Mosfet-Transistor man für ein Projekt einsetzen sollte. Und wo man die herbekommt. Deshalb soll hier eine Übersicht mit gängigen Mosfet-Transistoren und Bezugsquellen entstehen. Bezugsquellen sollten nach Möglichkeit solche sein, die auch für den privaten Bastler in Frage kommen.&lt;br /&gt;
&lt;br /&gt;
Der Thread zum Thema: http://www.mikrocontroller.net/topic/41588&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[Transistor-Übersicht]] - [[Dioden-Übersicht]] - [[Standardbauelemente]]&lt;br /&gt;
&lt;br /&gt;
== P-Kanal MOSFET==&lt;br /&gt;
Alles selbstsperrend, sog. Anreicherungstypen. &lt;br /&gt;
Bemerkung: sogenannte Verarmungstypen oder selbst leitende Mosfets sind in der P-Kanal Ausführung nicht für den &lt;br /&gt;
Konsumermarkt erhältlich. Diese sind nur in integrierten Schaltungen zu finden. &lt;br /&gt;
&lt;br /&gt;
{| {{Tabelle}} border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable sortable&amp;quot; id=&amp;quot;pkanalmosfets&amp;quot;&lt;br /&gt;
|- style=&amp;quot;background-color:#eeeeee&amp;quot;&lt;br /&gt;
! style=&amp;quot;width:15%;&amp;quot; | Name&lt;br /&gt;
! Gehäuse&lt;br /&gt;
! Her&amp;lt;br&amp;gt;steller&lt;br /&gt;
! &amp;lt;math&amp;gt;U_{GS(th)}&amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;/V&lt;br /&gt;
! &amp;lt;math&amp;gt;U_{DS}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;/V&lt;br /&gt;
! &amp;lt;math&amp;gt;I_D&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;/A&lt;br /&gt;
! &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;/W&lt;br /&gt;
! &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&amp;lt;math&amp;gt;m\Omega&amp;lt;/math&amp;gt;&lt;br /&gt;
! Bemerkung&lt;br /&gt;
! Lieferant&lt;br /&gt;
! Preis&amp;lt;br&amp;gt;/€&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLML2244 IRLML2244]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| IRF&lt;br /&gt;
| 1,1&lt;br /&gt;
| 20&lt;br /&gt;
| 4,3&lt;br /&gt;
| 1,3&lt;br /&gt;
| 54&lt;br /&gt;
| -&lt;br /&gt;
|[[Elektronikversender#TME_.28Transfer_Multisort_Elektronik.29|TME]],[[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Conrad|Con]],[[Elektronikversender#Farnell|Far]],[[Elektronikversender#Mouser|Mou]]&lt;br /&gt;
| 0,10 &lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BS250 BS250]&lt;br /&gt;
| TO-92, SOT-23&lt;br /&gt;
| Siliconix&lt;br /&gt;
| 4,0&lt;br /&gt;
| 60&lt;br /&gt;
| 0,12&lt;br /&gt;
| 0,35&lt;br /&gt;
| 10000&lt;br /&gt;
| -&lt;br /&gt;
|[[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Buerklin|Bü]]&lt;br /&gt;
| 0,32&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BSH205 BSH205]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| Phi&lt;br /&gt;
| 1,0&lt;br /&gt;
| 12&lt;br /&gt;
| 0,75&lt;br /&gt;
| 0,4&lt;br /&gt;
| 500&lt;br /&gt;
| Qg = 3,8nC&lt;br /&gt;
| [[Elektronikversender#csd-electronics|csd]] (a.A.)&lt;br /&gt;
| 0,30&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/SI2301 SI2301]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| Vishay&lt;br /&gt;
| 1,5&lt;br /&gt;
| 20&lt;br /&gt;
| 2,0&lt;br /&gt;
| 0,7&lt;br /&gt;
| 150&lt;br /&gt;
| Qg =4,5nC&lt;br /&gt;
| [[Elektronikversender#farnell|farnell]]&lt;br /&gt;
| 0,30&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLML6402  IRLML6402PBF]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| IRF&lt;br /&gt;
| 1,2&lt;br /&gt;
| 20&lt;br /&gt;
| 3,7&lt;br /&gt;
| 1,3&lt;br /&gt;
| 65&lt;br /&gt;
| 2,5V LL&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#csd-electronics|csd]],[[Elektronikversender#Buerklin|Bü]]&lt;br /&gt;
| 0,15&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLML6302  IRLML6302PBF]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| IRF&lt;br /&gt;
| 1,5&lt;br /&gt;
| 20&lt;br /&gt;
| 0,75&lt;br /&gt;
| 0,54&lt;br /&gt;
| 600&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#csd-electronics|csd]],[[Elektronikversender#Buerklin|Bü]]&lt;br /&gt;
| 0,18&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BSS83P BSS83P]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| Inf&lt;br /&gt;
| 2,0&lt;br /&gt;
| 60&lt;br /&gt;
| 0,33&lt;br /&gt;
| 0,36&lt;br /&gt;
| 2000&lt;br /&gt;
| nicht mit BSS83 (ohne P) verwechseln&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,11&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BSS84 BSS84]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| Fairchild,NXP&lt;br /&gt;
| 2,0&lt;br /&gt;
| 50&lt;br /&gt;
| 0,13&lt;br /&gt;
| 0,35&lt;br /&gt;
| 10000&lt;br /&gt;
| -&lt;br /&gt;
| [[Elektronikversender#Farnell|Fa]]&lt;br /&gt;
| 0,28&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BSS110 BSS110]&lt;br /&gt;
| TO-97, SOT-23&lt;br /&gt;
| Phi&lt;br /&gt;
| 3,0&lt;br /&gt;
| 50&lt;br /&gt;
| 0,17&lt;br /&gt;
| 0,35&lt;br /&gt;
| 10000&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/PMV65XP PMV65XP]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| Phi&lt;br /&gt;
| 1,4&lt;br /&gt;
| 20&lt;br /&gt;
| 3,9&lt;br /&gt;
| ?&lt;br /&gt;
| 76&lt;br /&gt;
| grosser ID für Bauform&lt;br /&gt;
| [[Elektronikversender#Spoerle|Spo]], [[Elektronikversender#RS_Components|RS]]&lt;br /&gt;
| 0,10&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF4905S IRF4905S]&lt;br /&gt;
| D2Pack&lt;br /&gt;
| IRF&lt;br /&gt;
| 4,0&lt;br /&gt;
| 55&lt;br /&gt;
| 64&lt;br /&gt;
| 3,8&lt;br /&gt;
| 20&lt;br /&gt;
| -&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]], [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 1,10&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF4905 IRF4905]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| IRF&lt;br /&gt;
| 4,0&lt;br /&gt;
| 55&lt;br /&gt;
| 74&lt;br /&gt;
| 200&lt;br /&gt;
| 20&lt;br /&gt;
| -&lt;br /&gt;
|[[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,93&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF5210 IRF5210]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| IRF&lt;br /&gt;
| 10,0&lt;br /&gt;
| 100&lt;br /&gt;
| 40&lt;br /&gt;
| 200&lt;br /&gt;
| 60&lt;br /&gt;
| -&lt;br /&gt;
|[[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 1,15&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF5210S IRF5210S]&lt;br /&gt;
| D2Pack&lt;br /&gt;
| IRF&lt;br /&gt;
| 10,0&lt;br /&gt;
| 100&lt;br /&gt;
| 40&lt;br /&gt;
| ?&lt;br /&gt;
| 60&lt;br /&gt;
| -&lt;br /&gt;
|[[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 1,25&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7205 IRF7205]&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 3,0&lt;br /&gt;
| 30&lt;br /&gt;
| 4,6&lt;br /&gt;
| 2,5&lt;br /&gt;
| 70&lt;br /&gt;
| ?&lt;br /&gt;
|[[Elektronikversender#Reichelt|Rei]] &lt;br /&gt;
| 0,34&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/FDC604P FDC604P]&lt;br /&gt;
| SuperSOT-6&lt;br /&gt;
| Fairchild&lt;br /&gt;
| 1,5&lt;br /&gt;
| 20&lt;br /&gt;
| 5,5&lt;br /&gt;
| 0,8-1,6&lt;br /&gt;
| 33&lt;br /&gt;
| -&lt;br /&gt;
| [[Elektronikversender#csd-electronics|csd]] (a.A.)&lt;br /&gt;
| 0,70&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/NDS0610 NDS0610]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| Fairchild&lt;br /&gt;
| 1,8&lt;br /&gt;
| 60&lt;br /&gt;
| 0,12&lt;br /&gt;
| 0,36&lt;br /&gt;
| 10000&lt;br /&gt;
| -&lt;br /&gt;
| [[Elektronikversender#csd-electronics|csd]]&lt;br /&gt;
| 0,07&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF5305 IRF5305]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| IRF&lt;br /&gt;
| 4,0&lt;br /&gt;
| 55&lt;br /&gt;
| 31&lt;br /&gt;
| 110&lt;br /&gt;
| 60&lt;br /&gt;
| -&lt;br /&gt;
|[[Elektronikversender#Reichelt|Rei]], [[Elektronikversender#CBoden|CBo]]&lt;br /&gt;
| 1,00&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/NDS352P NDS352P]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| Fairchild&lt;br /&gt;
| 4,5&lt;br /&gt;
| 20&lt;br /&gt;
| 0,85&lt;br /&gt;
| 0,5&lt;br /&gt;
| 500&lt;br /&gt;
| -&lt;br /&gt;
| [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 0,76&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BSP171 BSP171]&lt;br /&gt;
| SOT-223&lt;br /&gt;
| Siemens&lt;br /&gt;
| 1,4&lt;br /&gt;
| 60&lt;br /&gt;
| 1,7&lt;br /&gt;
| 1,8&lt;br /&gt;
| 350&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRFD9014 IRFD9014]&lt;br /&gt;
| HEXDIP, DIP4&lt;br /&gt;
| IRF&lt;br /&gt;
| 2,0&lt;br /&gt;
| 60&lt;br /&gt;
| 1,1&lt;br /&gt;
| 1,3&lt;br /&gt;
| 500&lt;br /&gt;
| -&lt;br /&gt;
| [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 0,65&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRFD9024 IRFD9024]&lt;br /&gt;
| HEXDIP, DIP4&lt;br /&gt;
| IRF&lt;br /&gt;
| 2,0&lt;br /&gt;
| 60&lt;br /&gt;
| 1,6&lt;br /&gt;
| 1,3&lt;br /&gt;
| 280&lt;br /&gt;
| -&lt;br /&gt;
| [[Elektronikversender#Conrad|Con]],[[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,50&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7416 IRF7416]&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 1,0&lt;br /&gt;
| 30&lt;br /&gt;
| 10&lt;br /&gt;
| 2,5&lt;br /&gt;
| 20&lt;br /&gt;
| -&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 0,43&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/SUP75P03 SUP75P03-007]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| Vishay&lt;br /&gt;
| 3,0&lt;br /&gt;
| 30&lt;br /&gt;
| 75&lt;br /&gt;
| 187&lt;br /&gt;
| 7&lt;br /&gt;
| -&lt;br /&gt;
|&lt;br /&gt;
| 2,30&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7220 IRF7220]&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 0,6&lt;br /&gt;
| 14&lt;br /&gt;
| 11&lt;br /&gt;
| 2,5&lt;br /&gt;
| 8,2&lt;br /&gt;
| 2,5V LL, Qg=84nC&lt;br /&gt;
| [[Elektronikversender#Conrad|Con]], [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,58&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7410 IRF7410]&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 0,4...0,9&lt;br /&gt;
| 12&lt;br /&gt;
| 16&lt;br /&gt;
| 2,5&lt;br /&gt;
| 7&lt;br /&gt;
| 1,8V LL, Qg=91nC &lt;br /&gt;
| [[Elektronikversender#Distrelec|Dis]], [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,85&lt;br /&gt;
|-&lt;br /&gt;
| AOC2413&lt;br /&gt;
| MCSP&lt;br /&gt;
| Alpha&amp;amp;Omega&lt;br /&gt;
| 0,42&lt;br /&gt;
| 8&lt;br /&gt;
| 3,5&lt;br /&gt;
| 0,55&lt;br /&gt;
| 28&lt;br /&gt;
| 1,2V LL, Qg=19nC &lt;br /&gt;
| [[Elektronikversender#Digi-Key|Digikey]]&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/AOD403 AOD403]&lt;br /&gt;
| D-Pak&lt;br /&gt;
| Alpha&amp;amp;Omega&lt;br /&gt;
| 1,5..3,5&lt;br /&gt;
| 30&lt;br /&gt;
| 70/12&lt;br /&gt;
| 90&lt;br /&gt;
| 8@10V&lt;br /&gt;
| Evtl.Alt.zu IRF4905/S&lt;br /&gt;
| Rei,[https://www.reichelt.de/mosfet-p-kanal-30-v-70-a-rds-on-0-0051-ohm-dpak-ao-d403-p166501.html?search=ao+d]&lt;br /&gt;
| 0,62&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
(Tabelle mit Click im Kopfbereich sortierbar; a.A. = Auf Anfrage, LL Logic Level, LIN Linearbetrieb möglich)&lt;br /&gt;
&lt;br /&gt;
== N-Kanal MOSFET==&lt;br /&gt;
Alles selbstsperrend, also Anreicherungstypen. Statt MOS-Verarmungstypen auf sFET/jFET(s.u.) zurückgreifen!&lt;br /&gt;
&lt;br /&gt;
{| {{Tabelle}} class=&amp;quot;wikitable sortable&amp;quot; id=&amp;quot;nkanalmosfets&amp;quot;&lt;br /&gt;
|- style=&amp;quot;background-color:#eeeeee&amp;quot;&lt;br /&gt;
! style=&amp;quot;width:15%;&amp;quot; | Name&lt;br /&gt;
! Gehäuse&lt;br /&gt;
! Her&amp;lt;br&amp;gt;steller&lt;br /&gt;
! &amp;lt;math&amp;gt;U_{GS(th)}&amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;/V&lt;br /&gt;
! &amp;lt;math&amp;gt;U_{DS}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;/V&lt;br /&gt;
! &amp;lt;math&amp;gt;I_D&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;/A&lt;br /&gt;
! &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;/W&lt;br /&gt;
! &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&amp;lt;math&amp;gt;m\Omega&amp;lt;/math&amp;gt;&lt;br /&gt;
! Bemerkung&lt;br /&gt;
! Lieferant&lt;br /&gt;
! Preis&amp;lt;br&amp;gt;/€&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLML6244 IRLML6244]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| IRF&lt;br /&gt;
| 0,5-1,1&lt;br /&gt;
| 20&lt;br /&gt;
| 6,3&lt;br /&gt;
| 1,3&lt;br /&gt;
| 21&lt;br /&gt;
! &lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,13&lt;br /&gt;
|-&lt;br /&gt;
| SI2302DS&lt;br /&gt;
| SOT-23&lt;br /&gt;
| Vishay&lt;br /&gt;
| 0,65-4,5&lt;br /&gt;
| 20&lt;br /&gt;
| 6&lt;br /&gt;
| 1,25&lt;br /&gt;
| 70&lt;br /&gt;
| 2,5V LL, sehr billig&lt;br /&gt;
| [[Elektronikversender#AliExpress|Ali]]&lt;br /&gt;
| 0,01&lt;br /&gt;
|-&lt;br /&gt;
| IRFP4310Z &lt;br /&gt;
| TO-247AC&lt;br /&gt;
| irf&lt;br /&gt;
| 2-4&lt;br /&gt;
| 100&lt;br /&gt;
| 120&lt;br /&gt;
| 280&lt;br /&gt;
| 4,8&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 1,80&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRFP450 IRFP450]&lt;br /&gt;
| TO-247&lt;br /&gt;
| irf&lt;br /&gt;
| 2,0-4,0&lt;br /&gt;
| 500&lt;br /&gt;
| 14&lt;br /&gt;
| 190&lt;br /&gt;
| 400&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 1,20&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF530 IRF530]&lt;br /&gt;
| TO-220&lt;br /&gt;
| irf&lt;br /&gt;
| 2,9&lt;br /&gt;
| 100&lt;br /&gt;
| 16&lt;br /&gt;
| 94&lt;br /&gt;
| 160&lt;br /&gt;
| LIN&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,44&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRL3103 IRL3103]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 1,0&lt;br /&gt;
| 30&lt;br /&gt;
| 64&lt;br /&gt;
| 94&lt;br /&gt;
| 12&lt;br /&gt;
| 4,5V LL, Qg=33nC (!)&lt;br /&gt;
| [[Elektronikversender#Segor-electronics|Seg]]&lt;br /&gt;
| 0,95&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF730A IRF730A]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 2,0-4,5&lt;br /&gt;
| 400&lt;br /&gt;
| 5,5&lt;br /&gt;
| 74&lt;br /&gt;
| 1000&lt;br /&gt;
| Qg=22nC (!)&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,54&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRFP064 IRFP064]&lt;br /&gt;
| TO-247AC&lt;br /&gt;
| irf&lt;br /&gt;
| 2,0&lt;br /&gt;
| 60&lt;br /&gt;
| 70&lt;br /&gt;
| 300&lt;br /&gt;
| 9&lt;br /&gt;
| Qg=190 nC&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 1,65&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF3205 IRF3205]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 2,0&lt;br /&gt;
| 55&lt;br /&gt;
| 110&lt;br /&gt;
| 200&lt;br /&gt;
| 8&lt;br /&gt;
|&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,69&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRL3803 IRL3803]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 1,0&lt;br /&gt;
| 30&lt;br /&gt;
| 140&lt;br /&gt;
| 200&lt;br /&gt;
| 6&lt;br /&gt;
| 4,5V LL, Qg=140nC&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,96&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF540 IRF540]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 3&lt;br /&gt;
| 100&lt;br /&gt;
| 28&lt;br /&gt;
| 150&lt;br /&gt;
| 77&lt;br /&gt;
| LIN&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Kessler|Kes]]&lt;br /&gt;
| 0,52&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7401 IRF7401]&lt;br /&gt;
| SO-8&lt;br /&gt;
| irf&lt;br /&gt;
| 2,7&lt;br /&gt;
| 20&lt;br /&gt;
| 8,7&lt;br /&gt;
| 2,0&lt;br /&gt;
| 22&lt;br /&gt;
| 2,7V LL&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]], [[Elektronikversender#CBoden|CBo]]&lt;br /&gt;
| 0,45&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7403 IRF7403]&lt;br /&gt;
| SO-8&lt;br /&gt;
| irf&lt;br /&gt;
| 4,85&lt;br /&gt;
| 30&lt;br /&gt;
| 8,5&lt;br /&gt;
| 2,5&lt;br /&gt;
| 22&lt;br /&gt;
| 4,5V LL&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,42&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7413 IRF7413]&lt;br /&gt;
| SO-8&lt;br /&gt;
| irf&lt;br /&gt;
| 3,0&lt;br /&gt;
| 30&lt;br /&gt;
| 13,0&lt;br /&gt;
| 2,5&lt;br /&gt;
| 11&lt;br /&gt;
| 4,5V LL&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Kessler|Kes]]&lt;br /&gt;
| 0,41&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BUZ11 BUZ11]&lt;br /&gt;
| TO-220&lt;br /&gt;
| ST&lt;br /&gt;
| 4,0&lt;br /&gt;
| 50&lt;br /&gt;
| 33,0&lt;br /&gt;
| 90,0&lt;br /&gt;
| 30&lt;br /&gt;
| LIN, SOA-Grenzen herstellerabhängig!&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,50&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BSS83 BSS83]&lt;br /&gt;
| SOT143&lt;br /&gt;
| NXP&lt;br /&gt;
| 2,0&lt;br /&gt;
| 10&lt;br /&gt;
| 0,05&lt;br /&gt;
| 0,23&lt;br /&gt;
| 45000&lt;br /&gt;
| nicht mit BSS83&#039;&#039;&#039;P&#039;&#039;&#039; verwechseln, Substratanschluss herausgeführt&lt;br /&gt;
| [[Elektronikversender#RS_Components|RS]],[[Elektronikversender#TME_.28Transfer_Multisort_Elektronik.29|TME]]&lt;br /&gt;
| 0,10&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/BS170 BS170]&lt;br /&gt;
| TO-92&lt;br /&gt;
&lt;br /&gt;
| gs&lt;br /&gt;
| 2,0&lt;br /&gt;
| 60&lt;br /&gt;
| 0,3&lt;br /&gt;
| 0,83&lt;br /&gt;
| 5000&lt;br /&gt;
| LIN&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,13&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BSN20 BSN20]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| gs&lt;br /&gt;
| 1,8&lt;br /&gt;
| 50&lt;br /&gt;
| 0,18&lt;br /&gt;
| 0,35&lt;br /&gt;
| 6000&lt;br /&gt;
| 4,5V LL, LIN&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,092&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BSS138 BSS138]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| div&lt;br /&gt;
| 0,8-1,6&lt;br /&gt;
| 50&lt;br /&gt;
| 0,22&lt;br /&gt;
| 0,36&lt;br /&gt;
| 2000&lt;br /&gt;
| 5V LL, LIN&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,06&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BSS123 BSS123]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| div&lt;br /&gt;
| 0,8-1,6&lt;br /&gt;
| 100&lt;br /&gt;
| 0,17&lt;br /&gt;
| 0,36&lt;br /&gt;
| 10000 @ 4,5V, &lt;br /&gt;
| 4,5V LL, LIN&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,06&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRFP2907 IRFP2907]&lt;br /&gt;
| TO-247AC&lt;br /&gt;
| irf&lt;br /&gt;
| 4,0&lt;br /&gt;
| 75&lt;br /&gt;
| 209&lt;br /&gt;
| 470&lt;br /&gt;
| 4,5&lt;br /&gt;
|&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 2,70&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/2N7000 2N7000]&lt;br /&gt;
| TO-92&lt;br /&gt;
| ON&lt;br /&gt;
| 3,0&lt;br /&gt;
| 60&lt;br /&gt;
| 0,2&lt;br /&gt;
| 0,35&lt;br /&gt;
| 5000&lt;br /&gt;
| 4,5V LL, LIN&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Kessler|Kes]]&lt;br /&gt;
| 0,13&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BS107 BS107]&lt;br /&gt;
| TO-92&lt;br /&gt;
| ON, Phi&lt;br /&gt;
| 3,0&lt;br /&gt;
| 200&lt;br /&gt;
| 0,25&lt;br /&gt;
| 0,35&lt;br /&gt;
| 6400/14000&lt;br /&gt;
| 2,6V LL; LIN&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,18&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BS108 BS108]&lt;br /&gt;
| TO-92&lt;br /&gt;
| ON, Phi&lt;br /&gt;
| 2,0&lt;br /&gt;
| 200&lt;br /&gt;
| 0,25&lt;br /&gt;
| 0,35&lt;br /&gt;
| 8000&lt;br /&gt;
| 2V LL; LIN&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,14&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BUK100 BUK100]&lt;br /&gt;
| TO-220&lt;br /&gt;
| Phi&lt;br /&gt;
| 3,0&lt;br /&gt;
| 50&lt;br /&gt;
| 13,5&lt;br /&gt;
| 40&lt;br /&gt;
| 125&lt;br /&gt;
| Überlast + &amp;lt;br&amp;gt;ESD-Schutz&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 1,40&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRL3705N IRL3705N]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 2,0&lt;br /&gt;
| 55&lt;br /&gt;
| 89&lt;br /&gt;
| 170&lt;br /&gt;
| 10&lt;br /&gt;
| 4V LL, Qg=98nC&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 1,20&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BUZ72 BUZ72A]&lt;br /&gt;
| TO-220&lt;br /&gt;
| Infineon&lt;br /&gt;
| 4,0&lt;br /&gt;
| 100&lt;br /&gt;
| 9,0&lt;br /&gt;
| 40&lt;br /&gt;
| 250&lt;br /&gt;
| 5V LL, LIN&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,45&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLZ34N IRLZ34N]&lt;br /&gt;
| TO-220&lt;br /&gt;
| irf&lt;br /&gt;
| 2,5&lt;br /&gt;
| 55&lt;br /&gt;
| 30&lt;br /&gt;
| 68&lt;br /&gt;
| 35&lt;br /&gt;
| 4V LL, LIN&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Farnell|Far]],[[Elektronikversender#Conrad|Con]],[[Elektronikversender#CBoden|CBo]] &lt;br /&gt;
| 0,39&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLZ44N IRLZ44N]&lt;br /&gt;
| TO-220&lt;br /&gt;
| irf&lt;br /&gt;
| 2&lt;br /&gt;
| 55&lt;br /&gt;
| 47&lt;br /&gt;
| 110&lt;br /&gt;
| 22&lt;br /&gt;
| 4V LL, LIN&lt;br /&gt;
| [[Elektronikversender#CBoden|CBo]] &lt;br /&gt;
| 0,49&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLML2502 IRLML2502]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| irf&lt;br /&gt;
| 1,2&lt;br /&gt;
| 20&lt;br /&gt;
| 4,2&lt;br /&gt;
| 1&lt;br /&gt;
| 45&lt;br /&gt;
| 2,5V LL&lt;br /&gt;
| [[Elektronikversender#csd-electronics|csd]] [[Elektronikversender#Reichelt|Rei (neu)]]&lt;br /&gt;
| 0,17&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF1404 IRF1404]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 4,0&lt;br /&gt;
| 40&lt;br /&gt;
| 202&lt;br /&gt;
| 333&lt;br /&gt;
| 4&lt;br /&gt;
| -&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 1,10&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRL1004 IRL1004]&lt;br /&gt;
| TO-220&lt;br /&gt;
| irf&lt;br /&gt;
| 2,7&lt;br /&gt;
| 40&lt;br /&gt;
| 130&lt;br /&gt;
| 200&lt;br /&gt;
| 6,5&lt;br /&gt;
| -&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 1,25&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRL530 IRL530]&lt;br /&gt;
| TO220, D2Pack&lt;br /&gt;
| irf&lt;br /&gt;
| 2&lt;br /&gt;
| 100&lt;br /&gt;
| 15,0&lt;br /&gt;
| 88&lt;br /&gt;
| 160&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,57&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF830 IRF830]&lt;br /&gt;
| TO220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 2,0-4,5   &lt;br /&gt;
| 500&lt;br /&gt;
| 5,0&lt;br /&gt;
| 74&lt;br /&gt;
| 1400&lt;br /&gt;
| LIN&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Kessler|Kes]]&lt;br /&gt;
| 0,57&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF840 IRF840]&lt;br /&gt;
| TO220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 2,0-4,0   &lt;br /&gt;
| 500&lt;br /&gt;
| 8,0&lt;br /&gt;
| 125&lt;br /&gt;
| 850&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,57&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/FDC645N FDC645N]&lt;br /&gt;
| SuperSOT-6&lt;br /&gt;
| Fairchild&lt;br /&gt;
| 1,5&lt;br /&gt;
| 30&lt;br /&gt;
| 5,5&lt;br /&gt;
| 0,8/1,6&lt;br /&gt;
| 30&lt;br /&gt;
| -&lt;br /&gt;
| [[Elektronikversender#csd-electronics|csd]] (a.A.), Far&lt;br /&gt;
| 0,7&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BSP297 BSP297]&lt;br /&gt;
| SOT-223&lt;br /&gt;
| Siemens/Infineon&lt;br /&gt;
| 0,8-2,4&lt;br /&gt;
| 200&lt;br /&gt;
| 0,65&lt;br /&gt;
| 1,8&lt;br /&gt;
| 6000&lt;br /&gt;
| 200V &amp;lt;math&amp;gt;U_{DS}&amp;lt;/math&amp;gt;, SMD und 4,5VLL, LIN (seltene Kombinaton)&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]], [[Elektronikversender#Schuricht|Schu]], [[Elektronikversender#RS_Components|RS]]&lt;br /&gt;
| 0,56&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7455 IRF7455]&lt;br /&gt;
| SO-8&lt;br /&gt;
| irf&lt;br /&gt;
| 4,5&lt;br /&gt;
| 30&lt;br /&gt;
| 15&lt;br /&gt;
| 2,5&lt;br /&gt;
| 7,5&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Kessler|Kes]]&lt;br /&gt;
| 1,04&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/SI4442DY SI4442DY]&lt;br /&gt;
| SO-8&lt;br /&gt;
| vis&lt;br /&gt;
| 2,5&lt;br /&gt;
| 30&lt;br /&gt;
| 22&lt;br /&gt;
| 2,5&lt;br /&gt;
| 5/4,5V&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Kessler|Kes]]&lt;br /&gt;
| 1,64&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLU2905 IRLU2905]&lt;br /&gt;
| TO251, DPack&lt;br /&gt;
| irf&lt;br /&gt;
| 2,0&lt;br /&gt;
| 55&lt;br /&gt;
| 42&lt;br /&gt;
| 110&lt;br /&gt;
| 27&lt;br /&gt;
| 4V LL&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Farnell|Far]],[[Elektronikversender#CBoden|CBo]]&lt;br /&gt;
| 0,54&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRFD014 IRFD014]&lt;br /&gt;
| HEXDIP/DIP4&lt;br /&gt;
| irf&lt;br /&gt;
| 2,0-4,0&lt;br /&gt;
| 60&lt;br /&gt;
| 1,7&lt;br /&gt;
| 1,3&lt;br /&gt;
| 200&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Conrad|Con]],[[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,52&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRFD024 IRFD024]&lt;br /&gt;
| HEXDIP/DIP4&lt;br /&gt;
| irf&lt;br /&gt;
| 2,0-4,0&lt;br /&gt;
| 60&lt;br /&gt;
| 2,5&lt;br /&gt;
| 1,3&lt;br /&gt;
| 100&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Conrad|Con]],[[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,54&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLD024 IRLD024]&lt;br /&gt;
| HEXDIP/DIP4&lt;br /&gt;
| irf&lt;br /&gt;
| 1,0-2,0&lt;br /&gt;
| 60&lt;br /&gt;
| 2,5&lt;br /&gt;
| 1,3&lt;br /&gt;
| 100&lt;br /&gt;
| 4V LL&lt;br /&gt;
| [[Elektronikversender#Conrad|Con]],[[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,47&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLU3717 IRLU3717]&lt;br /&gt;
| I-Pak&lt;br /&gt;
| irf&lt;br /&gt;
| 2,0&lt;br /&gt;
| 20&lt;br /&gt;
| 120&lt;br /&gt;
| 1,5/89&lt;br /&gt;
| 4&lt;br /&gt;
| 4,5V LL, Qg=21nC, &lt;br /&gt;
| [[Elektronikversender#Conrad|Con]],[[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 1,15&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRFP3703 IRFP3703]&lt;br /&gt;
| TO-247AC&lt;br /&gt;
| irf&lt;br /&gt;
| 4,0&lt;br /&gt;
| 30&lt;br /&gt;
| 210&lt;br /&gt;
| 230&lt;br /&gt;
| 2,8&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 5,08&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF3710 IRF3710]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 4&lt;br /&gt;
| 100&lt;br /&gt;
| 57&lt;br /&gt;
| 200&lt;br /&gt;
| 23&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 0,83&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLR7843 IRLR7843]&lt;br /&gt;
| D-Pack&lt;br /&gt;
| irf&lt;br /&gt;
| 2,3&lt;br /&gt;
| 30&lt;br /&gt;
| 164&lt;br /&gt;
| 140&lt;br /&gt;
| 3,3&lt;br /&gt;
| 4,5V LL, Qg: 34nC &lt;br /&gt;
|&lt;br /&gt;
| 0,70&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF1010N IRF1010N]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 4&lt;br /&gt;
| 55&lt;br /&gt;
| 85&lt;br /&gt;
| 180&lt;br /&gt;
| 11&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 1,99&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF1010Z IRF1010Z]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 4&lt;br /&gt;
| 55&lt;br /&gt;
| 75&lt;br /&gt;
| 140&lt;br /&gt;
| 7,5&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Conrad|Con]], [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 1,99&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLIZ44N IRLIZ44N]&lt;br /&gt;
| TO-220-Fullpak &lt;br /&gt;
| irf&lt;br /&gt;
| 1,0 - 2,0&lt;br /&gt;
| 55&lt;br /&gt;
| 30&lt;br /&gt;
| 45&lt;br /&gt;
| 25&lt;br /&gt;
| 4V LL&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 0,80&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLU024N IRLU024N]&lt;br /&gt;
| TO-251AA&lt;br /&gt;
| irf&lt;br /&gt;
| 1,0 - 2,0&lt;br /&gt;
| 55&lt;br /&gt;
| 17&lt;br /&gt;
| 45&lt;br /&gt;
| 80&lt;br /&gt;
| 4V LL, Qg=15 nC (!)&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 0,40&lt;br /&gt;
|-&lt;br /&gt;
| IRFZ48N&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 3&lt;br /&gt;
| 55&lt;br /&gt;
| 64&lt;br /&gt;
| 130&lt;br /&gt;
| 14&lt;br /&gt;
| &lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 0,60&lt;br /&gt;
|-&lt;br /&gt;
| [https://www.mikrocontroller.net/part/IRL2505 IRL2505]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 2,5&lt;br /&gt;
| 55&lt;br /&gt;
| 104&lt;br /&gt;
| &lt;br /&gt;
| 8&lt;br /&gt;
| 4V LL&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 3,99&lt;br /&gt;
|-&lt;br /&gt;
| [https://www.mikrocontroller.net/part/IRF7607 IRF7607]&lt;br /&gt;
| Micro8&lt;br /&gt;
| irf&lt;br /&gt;
| 1,2&lt;br /&gt;
| 20&lt;br /&gt;
| 6,5&lt;br /&gt;
| 1,8&lt;br /&gt;
| 30&lt;br /&gt;
| 2,5V LL&lt;br /&gt;
| [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 1,89&lt;br /&gt;
|-&lt;br /&gt;
| [https://www.mikrocontroller.net/part/IRF3708 IRF3708]&lt;br /&gt;
| TO-220AB&lt;br /&gt;
| irf&lt;br /&gt;
| 0,6 - 2&lt;br /&gt;
| 30&lt;br /&gt;
| 62&lt;br /&gt;
| 87&lt;br /&gt;
| 8&lt;br /&gt;
| 2,8V LL&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]],[[Elektronikversender#Conrad|Con]],[[Elektronikversender#CBoden|CBo]]&lt;br /&gt;
| 0,69&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/GF2304 GF2304]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| gs&lt;br /&gt;
| 1,0&lt;br /&gt;
| 30&lt;br /&gt;
| 2,5&lt;br /&gt;
| 1,25&lt;br /&gt;
| 135&lt;br /&gt;
| Qg=3,7nC&lt;br /&gt;
| [[Elektronikversender#Pollin_Electronic|Pol]]&lt;br /&gt;
| 0,05&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRLR8743 IRLR8743]&lt;br /&gt;
| I-Pak, D2Pack&lt;br /&gt;
| IRF&lt;br /&gt;
| 1,9&lt;br /&gt;
| 30&lt;br /&gt;
| 50&lt;br /&gt;
| 68&lt;br /&gt;
| 3,1&lt;br /&gt;
| 4,5V LL, Qg=39nC&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]] &lt;br /&gt;
| 1,15&lt;br /&gt;
|-&lt;br /&gt;
| AOC2414&lt;br /&gt;
| MCSP&lt;br /&gt;
| Alpha&amp;amp;Omega&lt;br /&gt;
| 0,52&lt;br /&gt;
| 8&lt;br /&gt;
| 4,5&lt;br /&gt;
| 0,55&lt;br /&gt;
| 19&lt;br /&gt;
| 1,2V LL, Qg=21,5nC&lt;br /&gt;
| Digikey &lt;br /&gt;
|-&lt;br /&gt;
| BSS131&lt;br /&gt;
| SOT-23&lt;br /&gt;
| Infineon&lt;br /&gt;
| 0,8-1,8&lt;br /&gt;
| 240&lt;br /&gt;
| 0,4&lt;br /&gt;
| 0,36&lt;br /&gt;
| 14000&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
(Tabelle mit Click im Kopfbereich sortierbar; a.A. Auf Anfrage, LL Logic Level, LIN Linearbetrieb möglich)&lt;br /&gt;
&lt;br /&gt;
== N-Kanal J-FET==&lt;br /&gt;
&lt;br /&gt;
{| {{Tabelle}} border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable sortable&amp;quot; id=&amp;quot;fetpaare&amp;quot;&lt;br /&gt;
|- style=&amp;quot;background-color:#eeeeee&amp;quot;&lt;br /&gt;
! style=&amp;quot;width:15%;&amp;quot; | Name&lt;br /&gt;
! Gehäuse&lt;br /&gt;
! Her&amp;lt;br&amp;gt;steller&lt;br /&gt;
! &amp;lt;math&amp;gt;U_{GS(co)}&amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;/V&lt;br /&gt;
! &amp;lt;math&amp;gt;U_{DS}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;/V&lt;br /&gt;
! &amp;lt;math&amp;gt;I_{D(max)}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;/mA&lt;br /&gt;
! Lieferant&lt;br /&gt;
! Preis&amp;lt;br&amp;gt;/€&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BF245 BF245A]&lt;br /&gt;
| TO-92&lt;br /&gt;
| obsolet&lt;br /&gt;
| -2,2&lt;br /&gt;
| 30&lt;br /&gt;
| 6,5&lt;br /&gt;
| &lt;br /&gt;
| 0,15 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BF245 BF245B]&lt;br /&gt;
| TO-92&lt;br /&gt;
| obsolet&lt;br /&gt;
| -3,8&lt;br /&gt;
| 30&lt;br /&gt;
| 15&lt;br /&gt;
| &lt;br /&gt;
| 0,15 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BF245 BF245C]&lt;br /&gt;
| TO-92&lt;br /&gt;
| obsolet&lt;br /&gt;
| -7,5&lt;br /&gt;
| 30&lt;br /&gt;
| 25&lt;br /&gt;
| &lt;br /&gt;
| 0,15 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BF246 BF246A]&lt;br /&gt;
| TO-92&lt;br /&gt;
| obsolet&lt;br /&gt;
| -4&lt;br /&gt;
| 25&lt;br /&gt;
| 80&lt;br /&gt;
| &lt;br /&gt;
| 0,15 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BF246 BF246B]&lt;br /&gt;
| TO-92&lt;br /&gt;
| obsolet&lt;br /&gt;
| -7&lt;br /&gt;
| 25&lt;br /&gt;
| 140&lt;br /&gt;
| &lt;br /&gt;
| 0,17 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BF246 BF246C]&lt;br /&gt;
| TO-92&lt;br /&gt;
| obsolet&lt;br /&gt;
| -12&lt;br /&gt;
| 25&lt;br /&gt;
| 250&lt;br /&gt;
| ?&lt;br /&gt;
| ?&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BF511 BF511]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| diverse&lt;br /&gt;
| -1,5&lt;br /&gt;
| 20&lt;br /&gt;
| 7&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,36 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BFR30 BFR30]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| diverse&lt;br /&gt;
| -4&lt;br /&gt;
| 25&lt;br /&gt;
| 10&lt;br /&gt;
| [[Elektronikversender#Kessler|Kes]]&lt;br /&gt;
| 0,29 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/BFR31 BFR31]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| diverse&lt;br /&gt;
| -2&lt;br /&gt;
| 25&lt;br /&gt;
| 5&lt;br /&gt;
| [[Elektronikversender#Kessler|Kes]]&lt;br /&gt;
| 0,29 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/MMBF4416 MMBF4416]&lt;br /&gt;
| SOT-23&lt;br /&gt;
| Fairchild&lt;br /&gt;
| -5,5&lt;br /&gt;
| 15&lt;br /&gt;
| 5&lt;br /&gt;
| [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 0,52 €&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
(Tabelle mit Click im Kopfbereich sortierbar)&lt;br /&gt;
&lt;br /&gt;
== FET-Paare ==&lt;br /&gt;
&lt;br /&gt;
{| {{Tabelle}} border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable sortable&amp;quot; id=&amp;quot;fetpaare&amp;quot;&lt;br /&gt;
|- style=&amp;quot;background-color:#eeeeee;writing-mode:sideways-lr;&amp;quot;&lt;br /&gt;
! Bezeichnung&lt;br /&gt;
! Package&lt;br /&gt;
! Hersteller&lt;br /&gt;
! U&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;/V&lt;br /&gt;
! U&amp;lt;sub&amp;gt;DS&amp;lt;/sub&amp;gt;/V&lt;br /&gt;
! I&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;/A&lt;br /&gt;
! P/W&lt;br /&gt;
! R&amp;lt;sub&amp;gt;DSon&amp;lt;/sub&amp;gt;/mΩ&lt;br /&gt;
! Bemerkung&lt;br /&gt;
! Lieferant&lt;br /&gt;
! Einzelpreis&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7389 IRF7389]&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 3,0&lt;br /&gt;
| 30&lt;br /&gt;
| 7,3/-5,3&lt;br /&gt;
| 2,0&lt;br /&gt;
| 29/58&lt;br /&gt;
| P+N&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,56 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7501 IRF7501]&lt;br /&gt;
| micro8&lt;br /&gt;
| IRF&lt;br /&gt;
| 2,7&lt;br /&gt;
| 20&lt;br /&gt;
| 2,4&lt;br /&gt;
| ?&lt;br /&gt;
| 135 @4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*N&lt;br /&gt;
| [[Elektronikversender#Kessler|Kessler]], [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 1,64 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7506 IRF7506]&lt;br /&gt;
| micro8&lt;br /&gt;
| IRF&lt;br /&gt;
| 4,5&lt;br /&gt;
| 30&lt;br /&gt;
| 1,7&lt;br /&gt;
| ?&lt;br /&gt;
| 270 @10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*P&lt;br /&gt;
| [[Elektronikversender#Kessler|Kessler]], [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 0,56 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7103 IRF7103]&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 4,5&lt;br /&gt;
| 50&lt;br /&gt;
| 2,3&lt;br /&gt;
| 2&lt;br /&gt;
| 130 @10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*N&lt;br /&gt;
|[[Elektronikversender#Reichelt|Rei]], [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 0,32 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7104 IRF7104]&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 4,5&lt;br /&gt;
| 20&lt;br /&gt;
| 2,3&lt;br /&gt;
| 2&lt;br /&gt;
| 250 @10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*P&lt;br /&gt;
|[[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,32 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7316 IRF7316]&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 4,5&lt;br /&gt;
| 30&lt;br /&gt;
| 4,9&lt;br /&gt;
| ?&lt;br /&gt;
| 58 @10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*P&lt;br /&gt;
|[[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,49 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRF7313 IRF7313]&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 4,5&lt;br /&gt;
| 30&lt;br /&gt;
| 6,5&lt;br /&gt;
| ?&lt;br /&gt;
| 46 @4.5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*N&lt;br /&gt;
| [[Elektronikversender#Kessler|Kessler]], [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 0,66 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/FDD8424H FDD8424H]&lt;br /&gt;
| Dual DPAK4L&lt;br /&gt;
| Fairchild&lt;br /&gt;
| &lt;br /&gt;
| 40&lt;br /&gt;
| 9/-6,5&lt;br /&gt;
| 3&lt;br /&gt;
| 24/54 @10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 30/70 @4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| P+N&lt;br /&gt;
| [[Elektronikversender#Digi-Key|Digi-Key]] &lt;br /&gt;
| 0,73 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/SUD50NP04-94 SUD50NP04-94]&lt;br /&gt;
| TO252-4L DPAK4L&lt;br /&gt;
| Vishay&lt;br /&gt;
| 2&lt;br /&gt;
| 40 &lt;br /&gt;
| 8&lt;br /&gt;
| 8?&lt;br /&gt;
| 41/53 @10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 45/72 @4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| P+N&lt;br /&gt;
| [[Elektronikversender#Farnell|Farnell]]&lt;br /&gt;
| 0,56 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7309&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| &amp;amp;nbsp;4,5 &amp;lt;br&amp;gt; -4,5&lt;br /&gt;
| &amp;amp;nbsp;30 &amp;lt;br&amp;gt; -30 &lt;br /&gt;
| &amp;amp;nbsp;4 &amp;lt;br&amp;gt; -3&lt;br /&gt;
| 1,4&lt;br /&gt;
| 50/100 @10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 80/160 @4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| P+N&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,38 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7307&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| &amp;amp;nbsp;2 &amp;lt;br&amp;gt; -2,5&lt;br /&gt;
| &amp;amp;nbsp;20 &amp;lt;br&amp;gt; -20 &lt;br /&gt;
| &amp;amp;nbsp;5,2 &amp;lt;br&amp;gt; -4,3&lt;br /&gt;
| 2&lt;br /&gt;
| 50/90 @4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 70/140 @2,7V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| P+N&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,47 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7343&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| &amp;amp;nbsp;3 &amp;lt;br&amp;gt; -3,5&lt;br /&gt;
| &amp;amp;nbsp;55 &amp;lt;br&amp;gt; -55 &lt;br /&gt;
| &amp;amp;nbsp;4,7 &amp;lt;br&amp;gt; -3,4&lt;br /&gt;
| 2&lt;br /&gt;
| 43/95 @10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 56/150 @4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| P+N&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,47 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7319&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| &amp;amp;nbsp;3 &amp;lt;br&amp;gt; -3,5&lt;br /&gt;
| &amp;amp;nbsp;30 &amp;lt;br&amp;gt; -30 &lt;br /&gt;
| &amp;amp;nbsp;6,5 &amp;lt;br&amp;gt; -4,9&lt;br /&gt;
| 2&lt;br /&gt;
| 23/42 @10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 32/76 @4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| P+N&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,50 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7314&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| -2,0&lt;br /&gt;
| -20 &lt;br /&gt;
| -5,3&lt;br /&gt;
| 2,0&lt;br /&gt;
| 49 @-4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 82 @-2,7V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*P&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,45 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7314&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| -2,0&lt;br /&gt;
| -20 &lt;br /&gt;
| -5,3&lt;br /&gt;
| 2,0&lt;br /&gt;
| 49 @-4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 82 @-2,7V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*P&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,45 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7304&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| -2,5&lt;br /&gt;
| -20&lt;br /&gt;
| -4,3&lt;br /&gt;
| 2,0&lt;br /&gt;
| 90 @-4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 140 @-2,7V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*P&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,46 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7306&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| -4,5&lt;br /&gt;
| -30&lt;br /&gt;
| -3,6&lt;br /&gt;
| 2,0&lt;br /&gt;
| 100 @-10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 160 @-4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*P&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,47 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7342&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| -3,5&lt;br /&gt;
| -55&lt;br /&gt;
| -3,4&lt;br /&gt;
| 2,0&lt;br /&gt;
| 95 @-10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 150 @-4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*P&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,50 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7328&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| -3,5&lt;br /&gt;
| -30&lt;br /&gt;
| -8,0&lt;br /&gt;
| 2,0&lt;br /&gt;
| 17 @-10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 26,8 @-4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*P&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,80 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7324&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| -1,5&lt;br /&gt;
| -20&lt;br /&gt;
| -9,0&lt;br /&gt;
| 2,0&lt;br /&gt;
| 18 @-4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 26 @-2,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*P&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,87 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7303&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 4,5&lt;br /&gt;
| 30 &lt;br /&gt;
| 4,9&lt;br /&gt;
| 2,0&lt;br /&gt;
| 50 @10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 80 @4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*N&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,43 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7301&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 2&lt;br /&gt;
| 20 &lt;br /&gt;
| 5,2&lt;br /&gt;
| 2,0&lt;br /&gt;
| 50 @4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 70 @2,7V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*N&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,46 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7341&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 3&lt;br /&gt;
| 55 &lt;br /&gt;
| 4,7&lt;br /&gt;
| 2,0&lt;br /&gt;
| 43 @10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 56 @4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*N&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,46 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7311&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 2&lt;br /&gt;
| 20 &lt;br /&gt;
| 6,6&lt;br /&gt;
| 2,0&lt;br /&gt;
| 23 @4,5V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; &amp;lt;br&amp;gt; 30 @2,7V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*N&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,60 €&lt;br /&gt;
|-&lt;br /&gt;
| IRF7380&lt;br /&gt;
| SO-8&lt;br /&gt;
| IRF&lt;br /&gt;
| 4,5&lt;br /&gt;
| 80 &lt;br /&gt;
| 3,6&lt;br /&gt;
| 2,0&lt;br /&gt;
| 61 @10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*N&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,57 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IRFI4024H-117P IRFI4024H-117P]&lt;br /&gt;
| TO-220-5&lt;br /&gt;
| IRF&lt;br /&gt;
| 2&lt;br /&gt;
| 55&lt;br /&gt;
| 11&lt;br /&gt;
| 14&lt;br /&gt;
| 50 @10V&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt;&lt;br /&gt;
| 2*N&lt;br /&gt;
| [[Elektronikversender#Reichelt|Reichelt]]&lt;br /&gt;
| 2,10 €&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
(Tabelle mit Click im Kopfbereich sortierbar)&lt;br /&gt;
&lt;br /&gt;
== MOSFET-Treiber ==&lt;br /&gt;
Nahezu alle MOSFET-Treiber haben eine der typischen Gatespannung von Leistungs-MOSFETs angepasste Speisespannung von 10..20 V. In der Regel werden sie mit 12 V betrieben. (Die Spannungsangabe in der Tabelle bezieht sich daher auf die &amp;lt;i&amp;gt;Brückenspeisespannung&amp;lt;/i&amp;gt; und gilt nur für High-Side-Treiber.) Davon unabhängig kann die Schwellspannung für Logikeingänge TTL-kompatibel sein (für Mikrocontroller) oder bei der halben Speisespannung liegen (für konventionelle Schaltungen).&lt;br /&gt;
&lt;br /&gt;
* Detaillierte [[Treiber]]-Dimensionierung&lt;br /&gt;
* Treiber-Typ:&lt;br /&gt;
** Low = Low-Side-Treiber (ohne Potenzialversatzstufe)&lt;br /&gt;
** High = High-Side-Treiber (&amp;lt;i&amp;gt;mit&amp;lt;/i&amp;gt; Potenzialversatzstufe)&lt;br /&gt;
** High + Low = beide Treiber mit &amp;lt;i&amp;gt;getrennten&amp;lt;/i&amp;gt; Logikeingängen&lt;br /&gt;
** Halbbrücke = beide Treiber mit &amp;lt;i&amp;gt;gemeinsamem&amp;lt;/i&amp;gt; Logikeingang und eingebautem Totzeitgenerator&lt;br /&gt;
** Vollbrücke = zwei Halbbrückentreiber&lt;br /&gt;
&lt;br /&gt;
{| {{Tabelle}} border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable sortable&amp;quot; id=&amp;quot;fettreiber&amp;quot;&lt;br /&gt;
|- style=&amp;quot;background-color:#eeeeee&amp;quot;&lt;br /&gt;
! Bezeichnung&lt;br /&gt;
! Treiber-Typ&lt;br /&gt;
! Strom&lt;br /&gt;
! Spannung&lt;br /&gt;
! Logikeingang&lt;br /&gt;
! Sockel&lt;br /&gt;
! Lieferant / Datenblatt&lt;br /&gt;
! Einzelpreis&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2101 IR2101]&lt;br /&gt;
| High + Low&lt;br /&gt;
| 130/270mA&lt;br /&gt;
| 600V&lt;br /&gt;
| 3,3V&lt;br /&gt;
| DIL8/SO8&lt;br /&gt;
| [[Elektronikversender#Conrad|Con]] &lt;br /&gt;
| 2,30 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2104 IR2104]&lt;br /&gt;
| Halbbrücke&lt;br /&gt;
| I&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt; = 130 mA&amp;lt;br&amp;gt;I&amp;lt;sub&amp;gt;L&amp;lt;/sub&amp;gt; = -270 mA&lt;br /&gt;
| 600V&lt;br /&gt;
| U&amp;lt;sub&amp;gt;L&amp;lt;/sub&amp;gt; ≤ 0,8 V&amp;lt;br&amp;gt;U&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt; ≥ 3 V&lt;br /&gt;
| DIL8/SO8&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&amp;lt;br&amp;gt;[[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 1,15 €&amp;lt;br&amp;gt;2,00 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2110 IR2110]&lt;br /&gt;
| High + Low&lt;br /&gt;
| 2A&lt;br /&gt;
| 500V&lt;br /&gt;
| 3,3V&lt;br /&gt;
| DIL14/SO16&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&amp;lt;br&amp;gt;[[Elektronikversender#Conrad|Con]] &lt;br /&gt;
| 1,30 €&amp;lt;br&amp;gt;1,55 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2111 IR2111]&lt;br /&gt;
| Halbbrücke&lt;br /&gt;
| 200/430mA&lt;br /&gt;
| 600V&lt;br /&gt;
| 10-20V CMOS Eingang&lt;br /&gt;
| DIL8/SO8, maximale Kriechwege&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 1,10 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2112 IR2112]&lt;br /&gt;
| High + Low&lt;br /&gt;
| 200/420mA&lt;br /&gt;
| 600V&lt;br /&gt;
| 3,3V&lt;br /&gt;
| DIL14/SO16&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]] &lt;br /&gt;
| 1,45 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2113 IR2113]&lt;br /&gt;
| High + Low&lt;br /&gt;
| 2A&lt;br /&gt;
| 600V&lt;br /&gt;
| 3,3V&lt;br /&gt;
| DIL14/SO16&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]] &lt;br /&gt;
| 1,85 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2117 IR2117]&lt;br /&gt;
| High&lt;br /&gt;
| 200/420mA&lt;br /&gt;
| 600V&lt;br /&gt;
| 10-20V CMOS Eingang&lt;br /&gt;
| DIL8/SO8, maximale Kriechwege&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]] &lt;br /&gt;
| 1,20 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2121 IR2121]&lt;br /&gt;
| Low&lt;br /&gt;
| 1A/2A&lt;br /&gt;
| -&lt;br /&gt;
| 2,5V&lt;br /&gt;
| DIL8/SO8, Strombegrenzung&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 2,15 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2125 IR2125]&lt;br /&gt;
| High&lt;br /&gt;
| 1A/2A&lt;br /&gt;
| 500V&lt;br /&gt;
| 2,5V&lt;br /&gt;
| DIL8/SO8, Strombegrenzung, maximale Kriechwege&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]] &lt;br /&gt;
| 4,20 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2127 IR2127]&lt;br /&gt;
| High&lt;br /&gt;
| 200/420mA&lt;br /&gt;
| ?&lt;br /&gt;
| 3,3V&lt;br /&gt;
| DIL8/SO8, maximale Kriechwege, Fehlerausgang&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]] &lt;br /&gt;
| 2,40 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2130 IR2130]&lt;br /&gt;
| 3-Phase Bridge&lt;br /&gt;
| 200/420mA&lt;br /&gt;
| 600V&lt;br /&gt;
| 2,5V&lt;br /&gt;
| DIL28/SO28&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]] &lt;br /&gt;
| 2,50 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2151 IR2151]&lt;br /&gt;
| Halbbrücke mit Oszillator&lt;br /&gt;
| 100/210mA&lt;br /&gt;
| 600V&lt;br /&gt;
| ?&lt;br /&gt;
| DIL8/SO8&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]] &lt;br /&gt;
| 2,50 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2153 IR2153]&lt;br /&gt;
| Halbbrücke mit Oszillator&lt;br /&gt;
| 100/210mA&lt;br /&gt;
| 600V&lt;br /&gt;
| n/a&lt;br /&gt;
| DIL8/SO8&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 1,20 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2155 IR2155]&lt;br /&gt;
| Halbbrücke mit Oszillator&lt;br /&gt;
| 210/420mA&lt;br /&gt;
| 600V&lt;br /&gt;
| n/a&lt;br /&gt;
| DIL8/SO8&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 3,50 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2181 IR2181]&lt;br /&gt;
| High + Low&lt;br /&gt;
| 1.4A/1.8A&lt;br /&gt;
| 600V&lt;br /&gt;
| 3,3V/5V&lt;br /&gt;
| DIL8/SO8&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 2,10 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2183 IR2183]&lt;br /&gt;
| Halbbrücke&lt;br /&gt;
| 1.4A/1.8A&lt;br /&gt;
| 600V&lt;br /&gt;
| 3,3V&lt;br /&gt;
| DIL8/SO8&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 4,00 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2184 IR2184]&lt;br /&gt;
| Halbbrücke&lt;br /&gt;
| 1.4A/1.8A&lt;br /&gt;
| 600V&lt;br /&gt;
| 3,3V/5V&lt;br /&gt;
| DIL8/SO8&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]] [[Elektronikversender#Conrad|Con]] &lt;br /&gt;
| 3,20 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/IR2136 IR2136]&lt;br /&gt;
| 3 Phase Bridge&lt;br /&gt;
| 120/250mA&lt;br /&gt;
| 600V&lt;br /&gt;
| 3,3V&lt;br /&gt;
| DIP28/SOIC28,&lt;br /&gt;
| [[Elektronikversender#Conrad|Con]]&lt;br /&gt;
| 1,80 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/ICL7667 ICL7667]&lt;br /&gt;
| 2 x Low&lt;br /&gt;
| ?&lt;br /&gt;
| 4.5-15V, 7Ω&lt;br /&gt;
| 3,3V&lt;br /&gt;
| DIL8/SO8 &lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 2,00 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/HIP4081A HIP4081A]&lt;br /&gt;
| Vollbrücke&lt;br /&gt;
| 2,5A&lt;br /&gt;
| 80V&lt;br /&gt;
| ?&lt;br /&gt;
| DIP20/SOIC20&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 8,00 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/HIP4082 HIP4082]&lt;br /&gt;
| Vollbrücke&lt;br /&gt;
| 1,2A&lt;br /&gt;
| 80V&lt;br /&gt;
| ?&lt;br /&gt;
| DIP16/SOIC16&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]] [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 6,20 €&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/HIP4083 HIP4083]&lt;br /&gt;
| 3 x High&lt;br /&gt;
| 0,3A&lt;br /&gt;
| 80V&lt;br /&gt;
| ?&lt;br /&gt;
| DIP16/SOIC16&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 4,80 €&lt;br /&gt;
&amp;lt;!-- ohne Preis und Lieferant&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/HIP4084 HIP4084]&lt;br /&gt;
| 4 Phase Bridge&lt;br /&gt;
| 0,5A&lt;br /&gt;
| 80V&lt;br /&gt;
| ?&lt;br /&gt;
| DIP28/SOIC28&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mikrocontroller.net/part/HIP4086 HIP4086]&lt;br /&gt;
| 3 Phase Bridge&lt;br /&gt;
| 0.5A&lt;br /&gt;
| 80V&lt;br /&gt;
| ?&lt;br /&gt;
| DIP24/SOIC24&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 8,00 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/TC4451 TC4451/TC4452]&lt;br /&gt;
| Low&lt;br /&gt;
| 12A peak, 2,5A DC&lt;br /&gt;
| ?&lt;br /&gt;
| 4,5-18V&lt;br /&gt;
| DIL8/SO8&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 3,00 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/LM5104 LM5104]&lt;br /&gt;
| Halbbrücke&lt;br /&gt;
| 1,6/1,8A&lt;br /&gt;
| 100V&lt;br /&gt;
| 2,5V&lt;br /&gt;
| SO8/LLP10 &lt;br /&gt;
| [[Elektronikversender#RS_Components|RS]]&lt;br /&gt;
| 3,20 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/MCP1407 MCP1407-E/P]&lt;br /&gt;
| Low&lt;br /&gt;
| 6A peak, 1,3A DC&lt;br /&gt;
| 4,5-18V&lt;br /&gt;
| 4,5-18V (VDD+0,3V)&lt;br /&gt;
| DIL8/SO8&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 0,93 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/MCP1416 MCP1416]&lt;br /&gt;
| Low&lt;br /&gt;
| 1.5A peak&lt;br /&gt;
| 4.5-18V&lt;br /&gt;
| 3.3V - VDD+0.3V&lt;br /&gt;
| SOT-23-5&lt;br /&gt;
| [[Elektronikversender#TME_.28Transfer_Multisort_Elektronik.29|TME]]&lt;br /&gt;
| 0,76 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/MAX626 MAX626]&lt;br /&gt;
| 2 x Low, inverting&lt;br /&gt;
| ?&lt;br /&gt;
| ?&lt;br /&gt;
| 4,5-18 V&lt;br /&gt;
| DIL8/SO8&lt;br /&gt;
| [[Elektronikversender#Reichelt|Rei]]&lt;br /&gt;
| 3,40 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/NCP5106 NCP5106]&lt;br /&gt;
| High + Low&lt;br /&gt;
| 250/500mA&lt;br /&gt;
| 600V&lt;br /&gt;
| 2,3-20V&lt;br /&gt;
| DIL8/SO8&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 2,37 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/NCP5304 NCP5304]&lt;br /&gt;
| High + Low&lt;br /&gt;
| 250/500mA&lt;br /&gt;
| 600V&lt;br /&gt;
| 2,3-20V&lt;br /&gt;
| DIL8/SO8&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 2,02 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/FAN7380 FAN7380]&lt;br /&gt;
| Halbbrücke&lt;br /&gt;
| 90/180mA&lt;br /&gt;
| 600V&lt;br /&gt;
| 2,5-25V&lt;br /&gt;
| SO8&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 0,99 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/FAN7382 FAN7382]&lt;br /&gt;
| High + Low&lt;br /&gt;
| 350/650mA&lt;br /&gt;
| 600V&lt;br /&gt;
| 2,9-20V&lt;br /&gt;
| DIL8/SO8/S014&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 1,25 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/FAN7361 FAN7361]&lt;br /&gt;
| Halbbrücke&lt;br /&gt;
| 250/500mA&lt;br /&gt;
| 600V&lt;br /&gt;
| 3,6-25V&lt;br /&gt;
| SO8&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 1,18 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/FAN7362 FAN7362]&lt;br /&gt;
| Halbbrücke&lt;br /&gt;
| 250/500mA&lt;br /&gt;
| 600V&lt;br /&gt;
| 2,9-25V&lt;br /&gt;
| SO8&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 1,71 €&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.mikrocontroller.net/part/FAN7842 FAN7842]&lt;br /&gt;
| High + Low&lt;br /&gt;
| 350/650mA&lt;br /&gt;
| 200V&lt;br /&gt;
| 2,9-25V&lt;br /&gt;
| SO8&lt;br /&gt;
| [[Elektronikversender#Farnell|Far]]&lt;br /&gt;
| 1,05 €&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Anmerkungen ==&lt;br /&gt;
* U&amp;lt;sub&amp;gt;GS(th)&amp;lt;/sub&amp;gt; - minimale Gatespannung, bei welcher der MOSFET zu leiten anfängt (100µA..1mA, nicht genormt). Zur vollständigen Durchschaltung bei maximalem Strom braucht es höhere Spannungen, siehe [https://www.mikrocontroller.net/articles/FET#Gate-Source_Threshold_Voltage Artikel FET].&lt;br /&gt;
* Logic Level - FET schaltet bei niedrigen Gatespannungen von typisch 4,5V (z.&amp;amp;nbsp;B. CMOS Logikpegel) hinreichend durch. Normale MOSFETs brauchen hierfür typisch 10V.&lt;br /&gt;
* U&amp;lt;sub&amp;gt;GS(co)&amp;lt;/sub&amp;gt; - Gate Source Cut Off Spannung, bei welcher der Drainstrom I&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; eines JFETs praktisch Null ist. Die Messbedingung ist jedoch nicht genormt (0,5nA..200µA).&lt;br /&gt;
* N-Kanal MOSFETs mit niedrigem R&amp;lt;sub&amp;gt;DS,On&amp;lt;/sub&amp;gt; sind technologisch einfacher herzustellen als P-Kanal MOSFETs. Deshalb gibt es bei P-Kanal keine so große Auswahl und oft werden Schaltungen angestrebt, in denen ausschließlich N-Kanal MOSFETs verwendet werden. Es gibt spezielle Treiberbausteine, die über eine Ladungspumpe für entsprechend hohe Gatespannung auch für die High-Side N-Fets sorgen (&amp;quot;Bootstrap Circuits&amp;quot;, siehe Artikel [[Treiber]]).&lt;br /&gt;
* Bei der Dimensionierung ist zu beachten, dass die Stromangabe im allgemeinen für 25°C gilt. Geht man davon aus, dass der MOSFET mit maximal zulässigem Strom betrieben wird und mit passend dimensioniertem [[Kühlkörper]] ausgestattet ist, so beträgt die Sperrschichttemperatur bis zu 150°C, folglich gilt z.&amp;amp;nbsp;B. für den IRF540 nicht mehr 28A, sondern nur noch ca. 12-15A.&lt;br /&gt;
* Restströme sind auch stark temperaturabhängig. Bei höherer Temperatur nehmen die Restströme exponentiell zu. So können bei 100°C durchaus 100 µA zwischen Source und Drain auch im gesperrten Zustand fließen. Bei 25°C ist dieser Reststrom meist bei 1µA spezifiziert. Real sind es meist weniger.&lt;br /&gt;
* Der Gate-Charge-Wert Qg (s. Datenblatt) bestimmt, wie schnell das Gate beim Schalten umgeladen werden kann. Auch wenn MOSFETs stromlos den durchgeschalteten Zustand halten können, braucht man während des Umschaltvorganges einen Strom, der das Gate umlädt (ähnlich wie ein Kondensator). Je höher dieser Strom, um so schneller ist der Umschaltvorgang und um so geringer die Verlustleistung während dieser Phase. Leistungs-MOSFETs können bei höheren Frequenzen (&amp;gt;1KHz) oft nur mit höheren Gateströmen von 0,1A-2A sinnvoll geschaltet werden. Man kann das Gate also nicht direkt an einen Digitalpin anschließen. Man braucht einen [[MOSFET-Übersicht#MOSFET-Treiber | MOSFET-Treiber]]. Manche MOSFETs haben eine sehr geringe Total Gate Charge (z.&amp;amp;nbsp;B. 4-10nC). Diese können in gewissen Grenzen recht gut direkt an digitalen [[Ausgangsstufen Logik-ICs | Logikausgängen]] betrieben werden,  ein praktisches Beispiel findet sich [http://www.mikrocontroller.net/topic/246449#2519459 hier]. Zur Abschätzung kann man sich merken: Wenn man das Gate eines MOSFETs mit einer Eingangskapazität von 1nF (~10nC) in 100ns auf 10V aufladen will, braucht man dazu 100mA.&lt;br /&gt;
&lt;br /&gt;
== Lieferantenübersicht ==&lt;br /&gt;
* [[Elektronikversender]]&lt;br /&gt;
* [[Elektronikversender#Reichelt|Rei]]&amp;lt;nowiki&amp;gt;chelt&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* [[Elektronikversender#elpro-Darmstadt|el]]&amp;lt;nowiki&amp;gt;pro&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* [[Elektronikversender#Conrad|Con]]&amp;lt;nowiki&amp;gt;rad&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* [[Elektronikversender#Kessler|Kes]]&amp;lt;nowiki&amp;gt;sler&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* [[Elektronikversender#csd-electronics|csd]]&amp;lt;nowiki&amp;gt;-electronics&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* [[Elektronikversender#Farnell|Far]]&amp;lt;nowiki&amp;gt;nell&amp;lt;/nowiki&amp;gt; (nur gewerbliche Kunden oder Studenten)&lt;br /&gt;
* [[Elektronikversender#Schuricht|Schu]]&amp;lt;nowiki&amp;gt;richt&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* [[Elektronikversender#RS_Components|RS]]&lt;br /&gt;
* [[Elektronikversender#Spoerle|Spo]]&amp;lt;nowiki&amp;gt;erle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Herstellerübersicht ==&lt;br /&gt;
* [irf] [http://www.irf.com International Rectifier]&lt;br /&gt;
* [Siliconix] [http://www.vishay.com/company/brands/siliconix/ Vishay Siliconix]&lt;br /&gt;
* [st] [http://www.st.com/web/en/home.html STMicroelectronics]&lt;br /&gt;
&lt;br /&gt;
== IRF MOSFET-Codierung ==&lt;br /&gt;
* IRF: Alle &amp;quot;Standardtransistoren&amp;quot;, also TO-220-Gehäuse&lt;br /&gt;
* IRFB: Hochspannungs-MosFETs&lt;br /&gt;
* IRFD: MosFETs im Dip-4-Gehäuse&lt;br /&gt;
* IRFI: MosFETs im isolierten TO-220-Gehäuse&lt;br /&gt;
* IRFP: MosFETs im TO-247AC-Gehäuse&lt;br /&gt;
* IRFR: MosFETs im D-Pak&lt;br /&gt;
* IRFU: MosFETs im I-Pak&lt;br /&gt;
* IRFZ: Also die die ich kenne liegen alle so bei 50-60V mit relativ niedrigem Rds(on), also so für mittlere Leistungen&lt;br /&gt;
* IRG:  Afaik sind das IGBTs&lt;br /&gt;
* IRL:  Logic-Level MosFETs&lt;br /&gt;
* IRLD: Logic-Level MosFETs im Dip-4 Gehäuse&lt;br /&gt;
* IRLI: Logic-Level MosFETs im isolierten TO-220-Gehäuse&lt;br /&gt;
* IRLR: Logic-Level MosFETs im D-Pak&lt;br /&gt;
* IRLU: Logic-Level MosFETs im I-Pak&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[FET]]&lt;br /&gt;
* [[IGBT]]&lt;br /&gt;
* [[Treiber]]&lt;br /&gt;
* [[H-Brücken Übersicht]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/280480#2960070 Forumsbeitrag]: Clevere MOSFET-Treiber mit kleinsten Trafos&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/283585#3004839 Forumsbeitrag]: Galvanisch getrennte Ansteuerung eines MOSFETs mittels Übertrager und 100% Tastverhältnis&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://www.sprut.de/electronic/switch/nkanal/nkanal.html N-Kanal MOSFET leicht erklärt bei sprut.de]&lt;br /&gt;
* [http://www.sprut.de/electronic/switch/pkanal/pkanal.html P-Kanal MOSFET leicht erklärt bei sprut.de]&lt;br /&gt;
* [http://elektronik-kompendium.de/sites/bau/0510161.htm MOSFET im ElKo]&lt;br /&gt;
* [http://elektronik-kompendium.de/public/schaerer/battoff.htm Abschaltverzögerung beim ElKo]&lt;br /&gt;
* [http://elektronik-kompendium.de/sites/bau/0207011.htm FET beim ElKo]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Mosfet MOSFET bei Wikipedia]&lt;br /&gt;
* [https://assets.nexperia.com/documents/technical-note/TN00008.pdf Power MOSFET frequently asked questions and answers (TN00008 von nexperia)]&lt;br /&gt;
&amp;lt;!-- * [http://www.irf.com/product-info/auto/autogdic.html IR21xx Familienvergleich] --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parametrische Suche beim Hersteller ==&lt;br /&gt;
* [http://www.infineon.com/cms/de/product/channel.html?channel=db3a304319c6f18c011a14e5341b25f1 Infineon]&lt;br /&gt;
* [http://www.nxp.com/#/ps/ps=%5Bi%3D48014%5D%7Cpp%3D%5Bt%3Dpfp%2Ci%3D48014%5D NXP standard Mosfets]&lt;br /&gt;
* [http://www.onsemi.com/PowerSolutions/parametrics.do?id=809&amp;amp;lctn=home ONsemi]&lt;br /&gt;
* [http://www.diodes.com/zetex/?ztx=3.0/3-3-1@tcatid~7 Diodes (vormals Zetex)]&lt;br /&gt;
* [http://www.irf.com/product-info/hexfet/ IRF]&lt;br /&gt;
* [http://www.vishay.com/mosfets/ Vishay]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Liste mit Bauteilen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Temperatursensor&amp;diff=107522</id>
		<title>Temperatursensor</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Temperatursensor&amp;diff=107522"/>
		<updated>2025-05-24T20:12:29Z</updated>

		<summary type="html">&lt;p&gt;Heha: Ratiometrische Messung ohne weitere aktive Bauelemente möglich und üblich&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Will man mit einem [[Mikrocontroller]] Temperaturen messen, dann braucht man&lt;br /&gt;
* einen [[Sensor]], der die Temperatur z.&amp;amp;nbsp;B. in eine Spannung oder einen Strom umsetzt&lt;br /&gt;
* einen [[ADC | AD-Wandler]], der das Signal digitalisiert. Der kann auf dem Sensor oder dem Mikrocontroller integriert sein.&lt;br /&gt;
&lt;br /&gt;
Temperatursensoren gibt es nun in allen möglichen Varianten. Vom temperaturabhängigen [[Widerstand]] bis zum fertig abgeglichenen All-in-one-Bauteil mit digitalem Ausgang. Wie bei allen Sensoren sollte man auch hier genau hinschauen und [[Auflösung und Genauigkeit]] unterscheiden.&lt;br /&gt;
&lt;br /&gt;
== Analoge Temperatursensoren ==&lt;br /&gt;
&lt;br /&gt;
=== Pt100 ===&lt;br /&gt;
&lt;br /&gt;
Unter einem Pt100 versteht man einen Platinwiderstand, der bei 0°C einen Widerstand von 100Ω hat.&lt;br /&gt;
Platinwiderstände sind temperaturabhängige Widerstände mit hoher Wiederholgenauigkeit und Konstanz[http://de.wikipedia.org/wiki/Konstante].  Wegen der relativ geringen Widerstandsänderung von nur ca. 0,4Ω pro Grad ist etwas mehr Schaltungsaufwand erforderlich als bei anderen Sensoren. Genauere Formeln zur Temperaturbestimmung gibt es u.a. bei [http://de.wikipedia.org/wiki/Pt100 Wikipedia]. Ein Schaltplan findet sich bei der [http://www.heise.de/ct/artikel/Sensibelchen-289608.html c&#039;t].&lt;br /&gt;
&lt;br /&gt;
Die Sensoren gibt es auch mit anderen Widerstandswerten, z.&amp;amp;nbsp;B. mit 1000&amp;amp;Omega; und heißen dann entsprechend Pt1000. Man setzt diese vor allem &#039;&#039;dann&#039;&#039; ein, wenn Eigenerwärmung oder Stromverbrauch eine Rolle spielt.&lt;br /&gt;
&lt;br /&gt;
Da bei der Entscheidung auf Pt100/Pt1000 Geld kaum eine Rolle spielt, entscheidet man sich für deren Auswertung häufig für fertige Schaltkreise mit passendem Verstärker und A/D-Wandler, wie dem MAX31865.&lt;br /&gt;
Im Interesse einer präzisen Funktion sollte dieser dennoch am besten mit einer vom Mikrocontroller entkoppelten stabilisierten Spannung betrieben werden.&lt;br /&gt;
Serienwiderstände in den I²C-Leitungen helfen, dass sich auch darüber weniger Störungen ausbreiten.&lt;br /&gt;
&lt;br /&gt;
Vorteil:&lt;br /&gt;
* genormt&lt;br /&gt;
* großer Meßbereich&lt;br /&gt;
* hohe Linearität&lt;br /&gt;
* hohe Wiederholgenauigkeit&lt;br /&gt;
* einfach austauschbar&lt;br /&gt;
&lt;br /&gt;
Nachteil:&lt;br /&gt;
* brauchen aufwändigere Auswerteschaltung&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [http://de.wikibooks.org/wiki/Linearisierung_von_resistiven_Sensoren/_Pt100 Wikibooks: Linearisierung von resistiven Sensoren - PT100]&lt;br /&gt;
* [https://www.heise.de/ct/artikel/Sensibelchen-289608.html c&#039;t-Artikel: Mikrocontroller-Programmierung: Timer, Sensoren und Drehgeber (mit PT100 Schaltung)]&lt;br /&gt;
* [http://www.maxim-ic.com/app-notes/index.mvp/id/3450 Maxim AN3450 Positive Analog Feedback Compensates PT100 Transducer]&lt;br /&gt;
* [http://www.maxim-ic.com/app-notes/index.mvp/id/4875 Maxim AN4875 High-Accuracy Temperature Measurements Call for Platinum Resistance Temperature Detectors (PRTDs) and Precision Delta-Sigma ADCs]&lt;br /&gt;
* [http://ww1.microchip.com/downloads/en/AppNotes/01154a.pdf Microchip AN1154 Precision RTD Instrumentation for Temperature Sensing]&lt;br /&gt;
* [http://www.analog.com/static/imported-files/application_notes/AN709_0.pdf Analog Devices AN709 RTD Interfacing and Linearization Using an ADuC8xx MicroConverter]&lt;br /&gt;
&lt;br /&gt;
=== NTC/PTC ===&lt;br /&gt;
&lt;br /&gt;
NTC und PTC sind temperaturabhängige Widerstände.&lt;br /&gt;
&lt;br /&gt;
* NTC (engl. &#039;&#039;&#039;N&#039;&#039;&#039;egative &#039;&#039;&#039;T&#039;&#039;&#039;emperature &#039;&#039;&#039;C&#039;&#039;&#039;oefficient, Heißleiter), hat bei hohen Temperaturen seinen niedrigsten Widerstand, z.&amp;amp;nbsp;B. Silizium&lt;br /&gt;
* PTC (engl. &#039;&#039;&#039;P&#039;&#039;&#039;ositive &#039;&#039;&#039;T&#039;&#039;&#039;emperature &#039;&#039;&#039;C&#039;&#039;&#039;oefficient, Kaltleiter), hat bei niedrigen Temperaturen seinen geringsten Widerstand, z.&amp;amp;nbsp;B. Glühlampe&lt;br /&gt;
&lt;br /&gt;
Um den Widerstandswert zu messen schaltet man sie mit einem normalen Widerstand oder einer [[Konstantstromquelle]] in Reihe zu einem [[Spannungsteiler]] und misst den Spannungsabfall.&lt;br /&gt;
Eine Beispielschaltung findet sich [http://www.mathar.com/msp_thermo1.html hier].&lt;br /&gt;
&lt;br /&gt;
Aufgrund des geringen Preises, der geradezu gigantischen Steilheit und der Verfügbarkeit mit hohen Widerstandswerten werden NTCs faktisch überall in der Heim- und Konsumgüterelektronik eingesetzt:&lt;br /&gt;
An der Heizungssteuerung (auch im Außenfühler), im Elektronik-Thermostatventil, in der Wetterstation (innen und außen), in der Gefriertruhe, im Auto, im Schaltschrank und in sämtlichen China-Schaltungen zur Temperaturregelung.&lt;br /&gt;
&lt;br /&gt;
Vorteil:&lt;br /&gt;
* billig (z.B. [https://www.reichelt.de/index.html?ACTION=446&amp;amp;LA=446&amp;amp;SEARCH=kty81 KTY81-x] bei Reichelt  0,52-0,75 &amp;amp;euro; 2018-01-03)&lt;br /&gt;
* Ein Vorwiderstand genügt für die direkte Anbindung an einen A/D-Wandler eines jeden modernen Mikrocontrollers. Dadurch ratiometrische Messung, keine Referenzspannung erforderlich. (Auflösung wird durch Bits des A/D-Wandlers bestimmt.)&lt;br /&gt;
&lt;br /&gt;
Nachteil:&lt;br /&gt;
* müssen für höhere Genauigkeiten abgeglichen werden&lt;br /&gt;
* brauchen A/D-Wandler&lt;br /&gt;
* sind nichtlinear&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* KTY10-5&lt;br /&gt;
* KTY13-6&lt;br /&gt;
* KTY81-121&lt;br /&gt;
* KTY81-122&lt;br /&gt;
* KTY81-210&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [http://www.sprut.de/electronic/temeratur/temp.htm Temperaturabhängige Stromquelle und NTC/PTC inclusive Linearisierung]&lt;br /&gt;
*[http://www.umnicom.de/Elektronik/Mikrokontroller/Atmel/AtFan/AtFan.html#2.2.2 Berechnung des Linearisierungswiderstandes für gewünschten Temperaturbereich] der fällt sonst immer vom Himmel&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/208587#2065880 KTY 10-5 Formelprobleme]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/225563 Codesammlung: Beispiel mit 0,5°C Auflösung]&lt;br /&gt;
* [http://preis-ing.de/extras/alle-berechnungen-im-schnellzugriff/automatisches-erzeugen-einer-ntc-tabelle/ Automatisches Erzeugen von C-Code zur NTC Auswertung] Aus den NTC Parametern und den Anforderungen wird  ein schneller und schlanker C-Code generiert.&lt;br /&gt;
&lt;br /&gt;
=== LMx35 ===&lt;br /&gt;
&lt;br /&gt;
Eine IC-Familie, die pro Kelvin Temperaturänderung ihre Ausgangsspannung um 10&amp;amp;nbsp;mV ändert. Die ICs gibt es in verschiedenen Genauigkeiten und Temperaturbereichen mit den Bezeichnungen LM135(A), LM235(A) und LM335(A). Der günstigste ist der LM335 mit einem Temperaturbereich von −40 … +100°C.&lt;br /&gt;
In verschiedenen Bauformen erhältlich. Beispielschaltungen finden sich im [http://www.national.com/ds.cgi/LM/LM135.pdf Datenblatt] und [http://www.suessbrich.info/elek/elektherm1.html hier]&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* hat auch ohne Kalibrierung eine Genauigkeit von einem Grad (bei 25°C)&lt;br /&gt;
* relativ billig (LM335 bei Reichelt ab 0,76&amp;amp;nbsp;€)&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* benötigt A/D-Wandler&lt;br /&gt;
* bei längerer Anschlussleitung störanfällig&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [http://www.schramm-software.de/tipps/temperaturmessung/analogsensoren.htm Test-Schaltungen und -Code zur Auswertung mit ADC (AVR-Assembler)]&lt;br /&gt;
&lt;br /&gt;
=== LM334 ===&lt;br /&gt;
&lt;br /&gt;
Ein IC ähnlich dem LM335 mit dem Unterschied, dass der durch das IC fließende Strom proportional von der Temperatur abhängt. Mit einer einfachen Schaltung aus nur zwei Widerständen kann man dann den Strom in einer Weise wandeln, dass pro Kelvin eine Spannungsänderung von 10mV ausgegeben wird. Da die Strom-Spannungswandlung auf der Platine (und damit nahe am AD-Wandler) stattfindet und die Übertragung des Messwerts durch einen Strom stattfindet, sind Störungen durch Netzbrummen etc. viel geringer als beim LM335&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
&amp;lt;!-- * hat auch ohne Kalibrierung eine Genauigkeit von einem Grad (bei 25°C) &lt;br /&gt;
Laut Datenblatt +-3°C&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
* relativ billig ([http://www.reichelt.de/?ARTICLE=10468 Reichelt 0,54 &amp;amp;euro;])&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* benötigt A/D-Wandler&lt;br /&gt;
* Bereich 0°C-70°C&lt;br /&gt;
&lt;br /&gt;
Ähnliche ICs:&lt;br /&gt;
* AD592 (Ausgangsstrom 1µA pro Kelvin, absolute Temperatur) [http://www.reichelt.de/?ARTICLE=3825 Reichelt: 3,75 €], Conrad 174912 8,50 &amp;amp;euro;&lt;br /&gt;
* B511N (geistern im Osten aus DDR-Beständen noch herum) wie AD592 aber deutlich mehr Parameterstreuung.&lt;br /&gt;
&lt;br /&gt;
=== SMT160-30 ===&lt;br /&gt;
&lt;br /&gt;
Ist ein Zwischending zwischen Digital und Analog. Sein Ausgangssignal ist ein digitales PWM-Signal, zu dessen Messung man am besten den Input-Capture-Eingang eines Mikrocontrollers verwendet. Man kann ihn also wie einen analogen Sensor nur indirekt auslesen, anstatt über einen AD-Wandler hier über einen Timer.&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Digitales PWM-Signal ist unempfindlich gegen Störeinflüsse&lt;br /&gt;
* gibt es in SO8, TO18, TO92 und &amp;lt;b&amp;gt;TO220&amp;lt;/b&amp;gt;, gut befestigbar, z.B am Kühlkörper&lt;br /&gt;
* linear&lt;br /&gt;
* kein Abgleich nötig&lt;br /&gt;
&lt;br /&gt;
Nachteile (viele):&lt;br /&gt;
* benötigt Timer&lt;br /&gt;
* jittert extrem, genaue Messungen nur über Mittelung / Filterung möglich&lt;br /&gt;
* nicht nur das PWM-Verhältnis, sondern auch die Frequenz ist temp-abhängig (1-4kHz)&lt;br /&gt;
* teuer (Farnell 10,90&amp;amp;euro; +16%, Conrad 9,xx&amp;amp;euro; , www.hy-line.de ??).&lt;br /&gt;
* TO92 Gehäuse ist günstiger, dafür weniger genau&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* http://www.hy-line.de/co/sensor-tec/hersteller/smartec/smt-160-30/index.html&lt;br /&gt;
&lt;br /&gt;
=== Thermoelement ===&lt;br /&gt;
&lt;br /&gt;
Ein Thermoelement besteht im einfachsten Fall aus zwei ungleichen Metallendrähten, die an einem Punkt miteinander verbunden sind und bei dem die Verbindungsstelle einer anderen Temperatur ausgesetzt ist als die offenen Enden der Drähte. An den offenen Enden der Drähten entsteht eine Spannung (Thermospannung). Dieser Effekt wurde 1821 von Thomas Seebeck entdeckt ([http://de.wikipedia.org/wiki/Seebeck-Effekt Seebeck-Effekt] bei Wikipedia). Eine weitere Anwendung ist der thermoelektrische Generator (&amp;quot;Thermogenerator&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Der herausragende Vorteil eines Thermoelements (meistens „Typ K“) ist seine obere Grenztemperatur von über 1000 °C.&lt;br /&gt;
Das Thermoelement degradiert durch Kristallveränderungen in der Schweiß- oder Pressverbindung oder durch Aufschmelzen.&lt;br /&gt;
Mit Thermoelementdraht (2 Drähte unterschiedlicher Metalle) lassen sich Sensoren flugs mit Aderendhülse + Zange&lt;br /&gt;
oder kleinem Schweißgerät problemspezifisch und sehr klein in der Abmessung herstellen.&lt;br /&gt;
&lt;br /&gt;
Ein Thermoelement ist selbst bei einfachen Digitalmultimetern mit Temperaturmessbereich beigefügt.&lt;br /&gt;
&lt;br /&gt;
Da Thermoelemente, genauso wie Pt100/Pt1000-Widerstandssensoren, eine hohe Verstärkung und einen hochauflösenden A/D-Wandler benötigen, setzt man auch hier gern fertige Schaltkreise ein, die gleich die Kaltstellenkompensation mitbringen.&lt;br /&gt;
&lt;br /&gt;
* MCP3421 18bit ADC 15SPS, I2C, auch mit andere Auflösungen erhältlich, Thermoelement kann direkt angeschlossen werden! (Reichelt : 2,10€)&lt;br /&gt;
&lt;br /&gt;
Vorteil:&lt;br /&gt;
* über einen sehr weiten Temperaturbereich einsetzbar&lt;br /&gt;
&lt;br /&gt;
Nachteil:&lt;br /&gt;
* die sehr geringen Temperaturspannungen im Mikrovoltbereich benötigen eine sehr gute Auswertelektronik (guter Analogteil + AD-Wandler). (aber: beachte MCP3421)&lt;br /&gt;
&lt;br /&gt;
* Misst nur Temperaturdifferenz&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&amp;lt;!-- Der Link wird von NI umgebogen zur Homepage. Recherche nach dem Autor brachte nix hervor :-(&lt;br /&gt;
* [http://digital.ni.com/worldwide/germany.nsf/web/all/7A4F02BAEFEC22AC802567F6003E0D6E  Temperaturmessung mit Thermoelementen] - Eine Einführung von David Potter (deutsche Überarbeitung: G.Sinkovic) (inkl. Erläuterung der Kaltstellenkompensation) --&amp;gt;&lt;br /&gt;
* [http://www.sensorwell.at/fileadmin/templates/images/data_sheets/temperatur_messtechnik.pdf Warum Thermoelemente Relativtemperaturen messen! oder Was ist eine Kaltstelle?] - Technische Information von www.sensorwell.at (PDF, ca. 600kB)&lt;br /&gt;
&lt;br /&gt;
=== P-N-Übergang ===&lt;br /&gt;
&lt;br /&gt;
Nicht direkt ein ausgemachter Sensor, aber jede normale Siliziumdiode kann als Temperatursensor herhalten.&lt;br /&gt;
Gängig ist dazu die Verwendung des P-N-Übergangs zwischen Basis und Emitter eines NPN-Transistors.&lt;br /&gt;
Das Verbinden von B und C bewirkt eine Stromverstärkung und damit eine geringere Abhängigkeit der Spannung vom Strom.&lt;br /&gt;
Siehe nächster Abschnitt.&lt;br /&gt;
&lt;br /&gt;
=== Im Mikrocontroller eingebauter Sensor ===&lt;br /&gt;
&lt;br /&gt;
Mikrocontroller mit A/D-Wandler bieten häufig einen eingebauten Temperatursensor an,&lt;br /&gt;
der mittels Eingangsmultiplexer auf den Wandler gegeben werden kann.&lt;br /&gt;
Die häufigste Implementierung dürfte einen in Durchlassrichtung arbeitenden P-N-Übergang benutzen.&lt;br /&gt;
An diesem beträgt der Temperaturkoeffizient -2 mV/K.&lt;br /&gt;
Ziemlich klein aber dafür hinreichend linear.&lt;br /&gt;
Im AVR-Mikrocontroller sind Auflösungen im Zehntelgradbereich illusorisch,&lt;br /&gt;
schon ganze Grad sind an der Grenze des Machbaren.&lt;br /&gt;
Die Hauptanwendung dieses Sensors ist wohl eher die Überwachung einer&lt;br /&gt;
leistungsintensiven Schaltung, bspw. mit Leistungstransistoren, &lt;br /&gt;
mit einer hellen LED (Fahrradscheinwerfer!) oder mit zu ladenden Batterien.&lt;br /&gt;
&lt;br /&gt;
=== indirekte Messung über die Temperaturabhängigkeit der Schallgeschwindigkeit ===&lt;br /&gt;
&lt;br /&gt;
Acoustic Thermometry Measures Temperature To 0.1 Degree&lt;br /&gt;
http://www.linear.com/solutions/1558&lt;br /&gt;
AN131  http://www.linear.com/docs/39793 (PDF momentan nicht verfügbar) --&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/topic/307557#new&lt;br /&gt;
&lt;br /&gt;
== Digitale Temperatursensoren ==&lt;br /&gt;
&lt;br /&gt;
=== AM2301 / DHT21 ===&lt;br /&gt;
&lt;br /&gt;
Low Cost One-Wire Sensoren&lt;br /&gt;
&lt;br /&gt;
* Relativ großer Sensor &lt;br /&gt;
* Stromversorgung: DC 3.3 - 5.2 V&lt;br /&gt;
* Ausgangssignal: 1-Draht-Bus - digitales Signal (One wire)&lt;br /&gt;
* Sensorelement: Polymer Feuchtigkeits-Kondensator&lt;br /&gt;
* Messbereich:    &lt;br /&gt;
** Luftfeuchtigkeit: 0 - 100% relative Luftfeuchte&lt;br /&gt;
** Temperatur: -40 °C  bis +80°C&lt;br /&gt;
* Genauigkeit:&lt;br /&gt;
** Luftfeuchtigkeit: +/- 3%&lt;br /&gt;
** Temperatur: +/- 0,5°C&lt;br /&gt;
* Auflösung:&lt;br /&gt;
** Feuchtigkeit: 0,1 % RH&lt;br /&gt;
** Temperatur: 0,1 °C&lt;br /&gt;
&lt;br /&gt;
Kleinere Version als DHT11 / DHT22 mit Drahtanschlüssen.&lt;br /&gt;
DHT11 :&lt;br /&gt;
* Genauigkeit:&lt;br /&gt;
** Luftfeuchtigkeit: +/- 5%&lt;br /&gt;
** Temperatur:       +/- 2°C&lt;br /&gt;
* Kosten: ca. 2,00 € (Ali) bis 8,00 € (eBay Deutschland)&lt;br /&gt;
&lt;br /&gt;
=== DS1621 ===&lt;br /&gt;
&lt;br /&gt;
Der DS1621 ist Temperatursensor und A/D-Wandler in einem. Er gibt seine Daten per [[I²C]]-[[Bus]] aus. Ein Schaltplan für einen elektronischen Thermometer mit diesem IC findet sich [http://www.myplace.nu/avr/thermo/ hier].&lt;br /&gt;
&lt;br /&gt;
Eine Library für den AVR: https://sourceforge.net/projects/ds1621avr/&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* bereits kalibriert&lt;br /&gt;
* kein A/D-Wandler nötig&lt;br /&gt;
* da I²C ein Bus ist, kann man mehrere DS1621 und andere I²C-Bausteine zusammen anschließen und braucht dafür trotzdem nur zwei I/O-Ports.&lt;br /&gt;
* Messbereich -55°C to +125°C &lt;br /&gt;
* Genauigkeit +-0,5°C&lt;br /&gt;
* Auflösung besser 0,01°C, wenn man die beiden Zählerregister (Count-Remain und Count-per-C) auswertet&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* teuer (Segor 5,80&amp;amp;euro;; RS 3,95&amp;amp;euro;; Conrad 5,22&amp;amp;euro;)&lt;br /&gt;
* obwohl die meisten Register [[Speicher#NVRAM | nichtflüchtig]] sind, kann man ihn nicht als Stand-Alone-Thermostat einsetzen, da er erst nach einem Start-Conversion-Befehl zu messen beginnt.&lt;br /&gt;
&lt;br /&gt;
Nachfolger:&lt;br /&gt;
* DS1631, DS1631A (Auto-Start-&amp;gt; Stand-Alone-Thermostat), DS1731&lt;br /&gt;
* weitere Stand-Alone-Thermostaten: DS1821, DS1629&lt;br /&gt;
&lt;br /&gt;
=== LM75 ===&lt;br /&gt;
&lt;br /&gt;
Der LM75 ist so ähnlich wie der DS1621, allerdings nur in SMD erhältlich und nicht so genau. Er ist aber öfters mal auf PC-Mainboards zu finden, so dass man beim Schlachten eines solchen günstig an einen Temperatursensor kommen kann. Eine Beispiel Schaltplan mit einem ATmega8 findet man [http://www.ucblog.de/2010/09/mikrocontroller-thermometer-schaltplan/ hier]&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* bereits kalibriert&lt;br /&gt;
* kein A/D-Wandler nötig&lt;br /&gt;
* I²C-Bus Ausgang&lt;br /&gt;
* billiger als DS1621 (Reichelt 1,45 &amp;amp;euro;; RS 3V: 3,75&amp;amp;euro;; 5V: 2,72&amp;amp;euro;)&lt;br /&gt;
* Auflösung +-0,5°C&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* nur im SMD-Gehäuse erhältlich&lt;br /&gt;
* relativ ungenau (+-2°C), kann man jedoch kalibrieren / kompensieren&lt;br /&gt;
&lt;br /&gt;
Kompatible Typen:&lt;br /&gt;
* AD7415ART&lt;br /&gt;
* DS7505S+&lt;br /&gt;
&lt;br /&gt;
=== LM76 ===&lt;br /&gt;
&lt;br /&gt;
Der LM76 ähnlich dem LM75, bietet aber eine 8-fach höhere Auflösung und eine Genauigkeit von 0.5 bzw. 1°C.&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* höhere Auflösung&lt;br /&gt;
* höhere Genauigkeit&lt;br /&gt;
&lt;br /&gt;
Nachteil:&lt;br /&gt;
* schwerer zu beschaffen&lt;br /&gt;
&lt;br /&gt;
=== TMP175 / TMP75 ===&lt;br /&gt;
&lt;br /&gt;
Ähnelt dem LM75 stark! Temperatursensor von Texas Instruments.&lt;br /&gt;
&lt;br /&gt;
EDIT 11.03.2024 Link funktioniert nicht mehr, Repo nicht mehr da.&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
[https://github.com/ManuelSchneid3r/RaspberryPi/blob/master/sensors/src/tmp.c Linux Kommandozeilen Tool für den Zugriff]&lt;br /&gt;
&lt;br /&gt;
=== DS18S20 / DS18B20 ===&lt;br /&gt;
&lt;br /&gt;
Der DS18S20 (Nachfolger des DS1820) und DS18B20 sind scheinbar Temperatursensoren und A/D-Wandler in einem. Wenn man genauer hinschaut, stellt man fest, dass es sich um direktwandelnde Sensoren handelt. Die Temperatur wird ohne Umweg über eine analoge Zwischengröße (Spannung oder Strom) in ein digitales Signal überführt.&lt;br /&gt;
&lt;br /&gt;
Die Datenkommunikation erfolgt über ein 1-Wire-Interface, wodurch man am [[Mikrocontroller]] mit nur einem einzigen I/O-Pin auskommen kann. Außerdem beherrschen sie die parasitäre Stromversorgung, d.h., man braucht für Daten und Stromversorgung zusammen nur zwei Leitungen.&lt;br /&gt;
&lt;br /&gt;
Beim DS18B20 sind Auflösungen von 9, 10, 11 und 12 Bits konfigurierbar. Je kleiner die Auflösung, desto kürzer ist die Messzeit. Der DS18S20 hat eine feste Auflösung von 12 Bits, wobei die unteren 4 Bits aufwändiger auszuwerten sind als beim DS18B20. Der DS18S20 ist als Ersatz für den DS1820 gedacht. Der Hersteller empfiehlt den DS18B20 für Neuentwicklungen.&lt;br /&gt;
&lt;br /&gt;
Anwendungshinweise bei Problemen&lt;br /&gt;
* An jeden Sensor einen 100 nF Keramikkondensator platzieren.&lt;br /&gt;
* Pull Up Widerstand am One Wire Bus kleiner machen, statt 4,7kΩ nur 2,2kΩ oder 1kΩ.&lt;br /&gt;
* Einen Widerstand von 10-100 Ohm in &#039;&#039;&#039;Reihe&#039;&#039;&#039; zum Mikrocontrollerausgang schalten, damit die Flanken flacher werden und Reflektionen unterdrückt werden, was vor allem bei längeren Leitungen &amp;gt;1m zum Problem werden kann.&lt;br /&gt;
* Dünne Anschlussleitungen verwenden, damit wenig Wärme durch die Anschlussleitungen übertragen wird. z.B: 0.2mm Kupferlackdraht verwenden, zumindest für die letzten 5 cm.&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* bereits kalibriert&lt;br /&gt;
* Genauigkeit +-0,5°C&lt;br /&gt;
* 1-Wire-Ausgang&lt;br /&gt;
&lt;br /&gt;
Preise:&lt;br /&gt;
* 2020: Reichelt: 1,60&amp;amp;euro; / CSD: 1,59&amp;amp;euro; / Conrad 5,08&amp;amp;euro;&lt;br /&gt;
* 2022-10-05 DS18S20: Reichelt 18,50&amp;amp;euro; / Pollin 12,95&amp;amp;euro; / Conrad: 28,99 &amp;amp;euro;&lt;br /&gt;
* 2022-10-05 DS18B20: Reichelt 3,80&amp;amp;euro; / Pollin 4,49 ; 7,90&amp;amp;euro; / Conrad: 7,99 &amp;amp;euro;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://www.ramser-elektro.at/1wire-sensor-zu-0-10v-konverter Zweikanaliger DS18B20 auf 0-10V Messumformer]&lt;br /&gt;
* [http://www.avr-projekte.de/ds18b20.htm Ein oder mehrere DS18B20 DS18S20 über Romcode einlesen. AVR-Assembler]&lt;br /&gt;
* [http://pic-projekte.de/wiki/index.php?title=Ansteuerung_eines_DS18S20 Ansteuern eines DS18S20 in C (PIC)]&lt;br /&gt;
* [http://pdfserv.maxim-ic.com/en/ds/DS18S20.pdf Datenblatt DS18S20] &lt;br /&gt;
* [http://pdfserv.maxim-ic.com/en/ds/DS18B20.pdf Datenblatt DS18B20]&lt;br /&gt;
* [http://www.maxim-ic.com/app-notes/index.mvp/id/4377 Vergleich DS18B20 &amp;lt;-&amp;gt; DS18S20]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/6505 Code zur Ansteuerung (ASM ATTiny12)]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/14792 Code zur Ansteuerung (AVR-GCC)]&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/494918 Fehlervermeidung]&lt;br /&gt;
* [http://gandalf.arubi.uni-kl.de/avr_projects/tempsensor/ Code zur Ansteuerung mit CRC-Prüfung (AVR-GCC)]&lt;br /&gt;
* [http://www.schramm-software.de/tipps/temperaturmessung/digitalsensoren.htm Code zur Ansteuerung mit CRC-Prüfung (AVR-Assembler)]&lt;br /&gt;
* [http://chaokhun.kmitl.ac.th/~kswichit/avrthermo/avrthermo.html LED-Thermometer mit AT90S2313 (C)]&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-248219.html Webserver zur Ansteuerung von bis zu 63 Bausteinen]&lt;br /&gt;
* [http://www.teslabs.com/openplayer/docs/docs/other/ds18b20_pre1.pdf PDF Anleitung zur Beschaltung und Programmierung (C)]&lt;br /&gt;
* [http://www.digitemp.com/building.shtml Anleitung Sensorfühleraufbau (DigiTemp)]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/14792 Forumsbeitrag]: Onewire + DS18x20 Ansteuerung in C&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/232156 Forumsbeitrag]: Timing der parasitären Versorgung&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/387139#4602608 Projekt]: Onewire + DS18x20 Bibliothek&lt;br /&gt;
&lt;br /&gt;
=== DS1822 ===&lt;br /&gt;
&lt;br /&gt;
Ähnlich wie DS18S20, aber weniger genau (+-2°) und in großen Stückzahlen billiger. Wegen der geringeren Verbreitung kommt der Preisvorteil aber bei Einzelstücken nicht beim Kunden an. So kostet er bei Reichelt mit 3,50&amp;amp;euro; mehr als der DS18S20.&lt;br /&gt;
&lt;br /&gt;
=== DS1921 / DS1922 ===&lt;br /&gt;
&lt;br /&gt;
Sind wie die DS1821 1-wire-Sensoren mit zusätzlicher Logging-Funktion.&lt;br /&gt;
Im iButton-Gehäuse befindet sich eine Lithium-Zelle, eine RTC, CMOS-RAM und der Temp-Sensor. Nach umfangreicher Programmierung startet der Button seine Mission (Aufzeichnung des Temperaturverlaufs).&lt;br /&gt;
Gibt es auch mit zusätzlicher Feuchtemessung (DS1923).&lt;br /&gt;
&lt;br /&gt;
=== TSic ===&lt;br /&gt;
&lt;br /&gt;
Die TSic Sensoren werden baugleich von 2 Herstellern angeboten:&lt;br /&gt;
* IST AG ([http://www.ist-ag.com/en/products-services/temperature-sensors Homepage])&lt;br /&gt;
* B+B Thermo-Technik ([https://shop.bb-sensors.com/Temperaturmesstechnik/Temperatursensoren/Digitaler-TSic-Temperatursensor-TO92.html Homepage])&lt;br /&gt;
&lt;br /&gt;
Die TSic Sensoren ([https://shop.bb-sensors.com/out/media/Datasheet_Digital_Semiconductor_temperatur_sensor_TSIC.pdf Datenblatt]) geben ihre Temperaturmessdaten automatisch in einem festen Intervall aus. Daher muss der Host nur warten bis die nächsten Messdaten rausgeschickt werden. Die TSic Sensoren die es im freien Handel gibt, geben ihre Messdaten alle 100ms (10Hz) aus. &lt;br /&gt;
Zur Übertragung wird das [http://www.ist-ag.com/eh/ist-ag/resource.nsf/imgref/Download_ZACWireAppNotes.pdf/$FILE/ZACWireAppNotes.pdf ZACwire] Protokoll benutzt. Es handelt sich um eine einfach zwei Byte Übertragung per Manchester-Code. Diese zwei Byte repräsentieren den digital gewandelten Temperaturwert. Im Gegensatz zu Sensoren wie den DS18xxx von Dallas muss dieser Wert aber erst auf einen dezimalen Wert umgerechnet werden. &lt;br /&gt;
Die Sensoren kommen mit 3 Pins aus (VCC, GND, Dout).&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Bereits kalibriert&lt;br /&gt;
* Verschiedene Genauigkeiten lieferbar&lt;br /&gt;
* Sehr einfaches Kommunikationsprotokoll&lt;br /&gt;
* Geringer Stromverbrauch&lt;br /&gt;
* Hochgenau: bis zu +/- 0.1°C (TSic 50x)&lt;br /&gt;
&lt;br /&gt;
Nachteil:&lt;br /&gt;
* Recht teuer (Reichelt: 4,70&amp;amp;euro; für den TSic206)&lt;br /&gt;
* Nur ein Sensor an einem I/O nutzbar (Kein Bussystem)&lt;br /&gt;
&lt;br /&gt;
Achtung! &lt;br /&gt;
Die TSic Sensoren gibt es auch als Version mit analog Ausgang. Bei der Typenbezeichnung gibt die 3. Stelle an ob es sich um die analog- oder Digitalversion handelt (1 = analog, 6 = digital). &lt;br /&gt;
Der TSic201 ist also analog, wärend der TSic206 ein digitaler ist.&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/283615#3025721 ZACwire Protokoll im Logic Analyzer]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/159774?goto=3157908#3157908 C-Code, (ohne Interrupt und ohne Timer) Sensor wird eingeschaltet-&amp;gt;gelesen-&amp;gt;ausgeschaltet]&lt;br /&gt;
* [http://www.avr-projekte.de/zacwireasm.htm Zacwire Protokoll, AVR-Assembler]&lt;br /&gt;
* [http://www.loetstelle.net/projekte2/tsic306/tsic306.php GCC AtMega8 Interruptgesteuerte Statemachine für TSIC206/306]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/45573#347765 Ansatz zum Empfang der Daten]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/225554# Beispiel mit Strobe ohne Interrupt]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/82087 Diskussion mit Beispielcode (MSP430, AVR, PIC) blockierend]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/144424#1367539 C++ Interrupt]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/88847 noch mehr C, problematisch Interrupt]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/151791#1426974 C für ATmega8]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/159149#1510455 auch problematisch]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/188462#1837622 fertiger Code zum Einlesen des Zacwire-Protokolls für PIC in ASM]&lt;br /&gt;
* [http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=55103 RN: Bascom]&lt;br /&gt;
* [http://www.avr-projekte.de/tinyclock.htm TSIC206 Thermometer mit Uhr und Kalender. Komplette Bauanleitung mit ASM Quellcode für AT-Tiny2313]&lt;br /&gt;
* [http://www.andeanelectronic.com/?Supporte___Arduino_con_TSic306%2CTSic506_y_TSic716 TSIC Routinen für Arduino von Andean Electronic aus Peru - Quelltext Dokumentation in Englisch]&lt;br /&gt;
&lt;br /&gt;
=== SHT3x ===&lt;br /&gt;
&lt;br /&gt;
Von [http://www.sensirion.com Sensirion] die aktuellen Versionen (2020) der Temperatur/Feuchte Sensoren&lt;br /&gt;
    &lt;br /&gt;
Vorteile:&lt;br /&gt;
* I2C&lt;br /&gt;
* Tempertur: +-0.3°C (SHT30) - +-0.1°C (SHT35)&lt;br /&gt;
* Temperaturbereich von -40 – +90°C&lt;br /&gt;
* Feuchtigkeit mit einer Genauigkeit von +-3 (SHT30) - 1.5%RH (SHT35)&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* nur als SMD-Package, gibt aber diverse Breakout-Boards zum Beispiel bei Adafruit oder Tindie [https://www.tindie.com/products/closedcube/sht35-d-digital-humidity-temperature-sensor/]&lt;br /&gt;
&lt;br /&gt;
=== SHT1x/SHT7x (End of Life) ===&lt;br /&gt;
&lt;br /&gt;
Der SHT1x/SHT7x (SHT10, SHT11, SHT15, STH71, SHT75) sind kombinierte Temperatur- und Feuchtesensoren von [http://www.sensirion.com Sensirion]. Sie unterscheiden sich in Bauform und Genauigkeit.&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* digitale Schnittstelle mit einfacher [[I²C]]-&#039;&#039;ähnlicher&#039;&#039; Ansteuerung&lt;br /&gt;
* keine Kalibrierung notwendig&lt;br /&gt;
* Beispielcode (C, MC51) auf der Sensirion-Seite verfügbar (relativ leicht portierbar)&lt;br /&gt;
* interne Heizelemente (Funktionsprüfung, &amp;quot;raue&amp;quot; Umgebung)&lt;br /&gt;
* Spannungsmonitor (&amp;quot;Battery fail&amp;quot;)&lt;br /&gt;
* sehr hohe Genauigkeit&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* kann nicht am [[I²C]] Bus betrieben werden, theoretisch gleiche Clockleitung möglich, fixe Adresse&lt;br /&gt;
* relativ teuer (Farnell 18,60&amp;amp;euro;)(SHT11 bei CSD 14€)&lt;br /&gt;
&lt;br /&gt;
[http://www.sensirion.com/de/produkte/feuchte-und-temperatur/ Übersicht] der Temperatur- und Feuchtigkeitssensoren von Sensirion.&lt;br /&gt;
&lt;br /&gt;
=== SHT21 (End of Life) ===&lt;br /&gt;
&lt;br /&gt;
[http://www.sensirion.com Sensirion] bietet auch den SHT21 Feuchtigkeits- und Temperatursensor an, welcher wesentlich genauer ist.&lt;br /&gt;
    &lt;br /&gt;
Vorteile:&lt;br /&gt;
* I2C digital, PWM and SDM/analog Volt Ausgabe&lt;br /&gt;
* Maximal 5 Messungen/s @ 14bit&lt;br /&gt;
* Temperaturbereich von -40 – +125°C&lt;br /&gt;
* Feuchtigkeit mit einer Genauigkeit von +-2%RH&lt;br /&gt;
* Günstig (3-4€ Farnell/RS 2014)&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* nur als SMD-Package&lt;br /&gt;
&lt;br /&gt;
Application Notes und Datenblätter findet man [http://www.sensirion.com/en/products/humidity-temperature/humidity-sensor-sht2x/ hier].&lt;br /&gt;
&lt;br /&gt;
=== ADT7310 / ADT7xxx-Familie von AD ===&lt;br /&gt;
&lt;br /&gt;
Der [http://www.analog.com/en/sensors/digital-temperature-sensors/adt7310/products/product.html ADT7310] von [http://www.analog.com/ Analog Devices] besitzt eine Auflösung von 16 Bit und eine Genauigkeit von ±0.5°C im Bereich von −40°C bis +105°C.&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Ansteuerung per [[SPI]] ADT73xx  oder  [[I2C]] ADT74xx&lt;br /&gt;
* keine Kalibrierung notwendig&lt;br /&gt;
* hohe [[Auflösung und Genauigkeit]]: 16 Bit&lt;br /&gt;
** ADT7x02 2,00°&lt;br /&gt;
** ADT7x01 1,00°&lt;br /&gt;
** ADT7x10 0,40 / 0,50°&lt;br /&gt;
** ADT7x20 0,20 / 0,25°&lt;br /&gt;
* auch für automotive / als Die lieferbar&lt;br /&gt;
* programmierbarer [[Interrupt]]ausgang für Unter- und Übertemperatur&lt;br /&gt;
* relativ günstig (ca. 3-8€ bei Digi-Key, Stand 12/2011)&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* TBD&lt;br /&gt;
&lt;br /&gt;
NB: ONsemi hat auch Temperatursensoren mit der Bezeichnung ADT7xxx, verwendet aber ein anderes Namensschema&lt;br /&gt;
&lt;br /&gt;
=== TSYS01 / G-NICO-018 von Measurement Specialties Inc. ===&lt;br /&gt;
* sehr hohe [[Auflösung und Genauigkeit]]:  0,10° @ -5°C … +50°C   16/24 Bit&lt;br /&gt;
* Gehäuse 16-VQFN Exposed Pad&lt;br /&gt;
* SPI / I2C über Pin auswählbar&lt;br /&gt;
* Preis: 8,60 (4,40 @1k) bei Digikey&lt;br /&gt;
kleiner Nachteil: der Sensor liefert den ADC-Wert und die Kompensationskonstanten (5 Polynom-Koeffizienten), mit denen im µC die Temperatur berechnet werden muss.&lt;br /&gt;
&lt;br /&gt;
=== SE95 ===&lt;br /&gt;
&lt;br /&gt;
Der [http://www.nxp.com/documents/data_sheet/SE95.pdf SE95] von NXP hat 13 Bit Auflösung, Genauigkeit ±2°C im Messbereich von -55°C bis +125°C, I²C, Gehäuse SO8 und TSSOP8. Einzelstück-Preis bei Segor 1,50€ (2012/I)&lt;br /&gt;
&lt;br /&gt;
=== Bosch Sensortec BMP085 / BMP180 ===&lt;br /&gt;
Die BMP085 (bzw. der verbesserte, aber Pin- und Software-komapatible Nachfolger BMP180) sind eigentlich Luftdrucksensoren, die jedoch auch einen Temperatursensor mitbringen. Der Anschluss erfolgt über I2C.&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Wenig Stromverbrauch (5µA bei 1 Messung/s)&lt;br /&gt;
* Liefern Luftdruck gleich mit&lt;br /&gt;
* Absolute Genauigkeit (+-1°C typ. über kompletten Temperaturbereich). Relative Genauigkeit ist im Datenblatt nicht spezifiziert, gemessen gegen einen SHT11 ca. +-0.1°C. &lt;br /&gt;
* Auflösung: 0.1°C mit Herstellercode, mehr ist möglich (16Bit)&lt;br /&gt;
* Fertige Platinen für wenig Geld verfügbar (ca. 1,70€ inkl. Versand aus China)&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Sehr aufwändige Linearisierung (Kalibrationskoeffizienten sind im Sensor gespeichert, müssen aber vom Host-µC verrechnet werden. Code ist im Datenblatt)&lt;br /&gt;
* Nur als SMD&lt;br /&gt;
* Bei Reichelt und Conrad nicht erhältlich. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Preis: Ebay 1,70€ (mit Platine, China), Aliexpress 1,10€ (nackter Chip, China, mind. 10 Stück sonst auch ca. 1,70€)&lt;br /&gt;
&lt;br /&gt;
=== Bosch Sensortec BME280 / BME680 ===&lt;br /&gt;
* I2C-Sensor für Temperatur, Luftfeuchtigkeit, Luftdruck (BME680 = BME280 + Luftgüte)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Breakout-Board BME280 Aliexpress ~2,20€, BME680 Watterott 15,95€&lt;br /&gt;
* Bosch-eigene Bibliotheken für gängige Mikrocontroller, Android und RaspberryPi&lt;br /&gt;
* Arduino-Bibliotheken von Adafruit, Sparkfun und Watterott&lt;br /&gt;
* Minimaler Platzbedarf&lt;br /&gt;
* SMT ermöglicht doppelseite Platine mit BME280/BME680 auf Frontseite und restlicher Elektronik auf Rückseite&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Ball-Grid-Array Reflow, manuelles Löten nur mit Breakout-Board oder Vias möglich&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bibliotheken/Datenblätter/Herstellerinformation:&lt;br /&gt;
* BME280: https://www.bosch-sensortec.com/bst/products/all_products/bme280&lt;br /&gt;
* BME680: https://www.bosch-sensortec.com/bst/products/all_products/bme680&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Projekte/Code&lt;br /&gt;
* [ASM] [https://www.mikrocontroller.net/topic/506743#6473604 BME280 mit OLED Display am ATmega808]&lt;br /&gt;
* [ASM] [https://www.mikrocontroller.net/topic/506743#6495985 BME280 mit OLED Display am ATtiny85]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Preis: BME280 / BME680 Reichelt 5,20€ / 9,45€, Conrad 8,33€ / xxx, Aliexpress ~2,50€ / ~15,-€&lt;br /&gt;
&lt;br /&gt;
=== HDC1080 ===&lt;br /&gt;
Der HDC1080 von Texas Instruments kombiniert einen digitalen Luftfeuchtesensor mit Temperaturmessung. Der Anschluss erfolgt über IC2.&lt;br /&gt;
&lt;br /&gt;
Merkmale:&lt;br /&gt;
* geringe Stromaufnahme (1,3 µA bei 1 Messung/s)&lt;br /&gt;
* gleichzeitige Luftfeuchtemessung möglich&lt;br /&gt;
* Spannungsversorgung: 2,7 bis 5,5 V&lt;br /&gt;
* Genauigkeit: +/-0,2 °C typ. (im Temperaturbereich zwischen 5 bis 60 °C)&lt;br /&gt;
* Auflösung Temperatur: 11/14 bit wählbar&lt;br /&gt;
* Maximale Dauer einer Messung: 6,5 ms&lt;br /&gt;
* eingebautes Heizelement (gegen Feuchtigkeitskondensation, per Konfigurationsregister einschaltbar)&lt;br /&gt;
* Herstellerseitig kalibriert&lt;br /&gt;
* einfache Umrechnung&lt;br /&gt;
* Gehäuse: SMD 6-Pin PWSON (3x3 mm)&lt;br /&gt;
&lt;br /&gt;
Erhältich als Pmod (&#039;&#039;PmodHygro&#039;&#039; von Digilentic) und in Breakoutboards zahlreicher anderer Hersteller.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== ScioSense ENS21x ===&lt;br /&gt;
&lt;br /&gt;
Vom deutsch-holländischen Hersteller [http://www.sciosense.com ScioSense] werden mit der [https://www.sciosense.com/ens21x-family-of-high-performance-digital-temperature-and-humidity-sensors/ ENS21x Familie] kombinierte hochpräzise Feuchte- und Temperatursensoren mit I²C Interface angeboten. Dabei gibt es je nach Anforderung unterschiedliche Genauigkeitsklassen, die sich im Produktname klassifizieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Merkmale:&lt;br /&gt;
&lt;br /&gt;
* Stromaufnahme für RH und T Messung bei ~6.6µA @ 1Hz&lt;br /&gt;
* Spannungsbereich 1.7V....4.7V&lt;br /&gt;
* Temperaturbereich -40..125°C, relative Feuchte 0..100% rH&lt;br /&gt;
* Genauigkeit Temperatur +/- 0.1°C, 0.8%RH (ENS215) ...2.0%RH (ENS210)&lt;br /&gt;
* kontinuierliche Messung Feuchte und Temperatur möglich; Sensoren sind fertig kalibriert&lt;br /&gt;
* Ausgabedaten sind fertig aufbereitet, keine externe Kompensation o.ä. nötig&lt;br /&gt;
* QFN4 package (2.0 x 2.0 x 0.75mm3)&lt;br /&gt;
* GitHub/Arduino Integration mit Beispielcode&lt;br /&gt;
&lt;br /&gt;
Erhältlich bei Mouse und Digikey, ebenso EVKit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nenben den Temperatur/Feuchtesensoren sind auch hochpräzise barometrische Drucksensensoren ([https://www.sciosense.com/ens220-barometric-pressure-and-temperature-sensor/ ENS220]) sowie MOX-Gassensoren für Luftqualitätsbestimmung ([https://www.sciosense.com/ens16x-digital-metal-oxide-multi-gas-sensor-family/ ENS16x]) erhältlich, sowie weiter Kombinationen mit Partikel oder Flowsensoren.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Preisübersicht ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Preisübersicht 11/2012  teilw. Update 10/2024&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Bauteil   || Segor || RS-Components || Conrad || Farnell || Reichelt || DigiKey || Mouser&lt;br /&gt;
|-&lt;br /&gt;
| AD592     || 21,40 || 5,74          || 6,43   || 5,90    || 3,95     || 4,61&lt;br /&gt;
|-&lt;br /&gt;
| ADT7310   ||  -    || -             || -      || 4,56    || -        || 3,02&lt;br /&gt;
|-&lt;br /&gt;
| ADT7410   ||  -    || 3,35          || -      || 2,71    || -        || 3,02&lt;br /&gt;
|-&lt;br /&gt;
| DS1621    ||  7,60 || 5,29          || 5,08   || 5,65    || -        || 4,54&lt;br /&gt;
|-&lt;br /&gt;
| DS1629    ||  8,70 || 8,68          || -      || 5,00    || 6,50     || 7,65&lt;br /&gt;
|-&lt;br /&gt;
| DS1631    ||  8,00 || 2,91          || -      || 3,11    || -        || 3,94&lt;br /&gt;
|-&lt;br /&gt;
| DS1731    ||  -    || -             || -      || 9,79    || -        || 3,81&lt;br /&gt;
|-&lt;br /&gt;
| DS1821    ||  5,90 || 5,27          || 6,27   || -       || -        || 5,05&lt;br /&gt;
|-&lt;br /&gt;
| DS18B20   ||  2,50 || 3,06          || -      || 3,26    || 3,20     || 3,93&lt;br /&gt;
|-&lt;br /&gt;
| DS1921    ||  -    || 26,15         || -      || -       || -        || 21,13&lt;br /&gt;
|-&lt;br /&gt;
| DS1922    ||  -    || 62,00         || -      || -       || -        || 43,11&lt;br /&gt;
|-&lt;br /&gt;
| DS1923    ||  -    || 97,96         || -      || -       || -        || 80,30&lt;br /&gt;
|-&lt;br /&gt;
| KTY81/121 ||  -    || -             || -      || 0,79    || 0,59     || 0,85&lt;br /&gt;
|-&lt;br /&gt;
| LM75      ||  1,50 || 0,68          || 3,64   || 0,81    || 1,45     || 0,82&lt;br /&gt;
|-&lt;br /&gt;
| LM76      ||  -    || 2,83          || -      || 3,02    || -        || 2,39&lt;br /&gt;
|-&lt;br /&gt;
| LM135     || 10,10 || 10,28         || -      || 8,95    || 7,95     || 10,26&lt;br /&gt;
|-&lt;br /&gt;
| LM235     ||  -    || -             || -      || 1,80    || 1,40     || 1,26&lt;br /&gt;
|-&lt;br /&gt;
| LM334     ||  0,90 || 0,72          || 1,67   || 1,01    || 0,49     || 0,74&lt;br /&gt;
|-&lt;br /&gt;
| LM335     ||  1,30 || 0,56          || 1,92   || 0,55    || 0,71     || 0,80&lt;br /&gt;
|-&lt;br /&gt;
| PT100     ||  3,80 || 3,99          || 4,00   || 9,79    || 4,27     || 0,68&lt;br /&gt;
|-&lt;br /&gt;
| SE95      ||  1,50 || 0,63          || -      || 1,45    || -        || 1,00&lt;br /&gt;
|-&lt;br /&gt;
| SHT11     || 26,00 || 24,38         || 33,20  || 25,65   || -        || 36,76&lt;br /&gt;
|-&lt;br /&gt;
| SHT15     ||  -    || 27,69         || 36,30  || 28,72   || -        || -&lt;br /&gt;
|-&lt;br /&gt;
| SHT21     || 29,50 || 18,10         || -      || 21,16   || -        || -&lt;br /&gt;
|-&lt;br /&gt;
| SHT71     || 25,40 || 29,06         || 36,89  || 30,88   || -        || -&lt;br /&gt;
|-&lt;br /&gt;
| SHT75     ||  -    || 33,77         || 42,48  || 35,52   || -        || -&lt;br /&gt;
|-&lt;br /&gt;
| SMT160-30 ||  -    || 9,28          || 8,14   || 12,38   || -        || -&lt;br /&gt;
|-&lt;br /&gt;
| TMP75     ||  -    || 0,80          || -      || 0,85    || -        || 1,37&lt;br /&gt;
|-&lt;br /&gt;
| TSic206   ||  -    || -             || 5,84   || -       || 4,20     || -&lt;br /&gt;
|-&lt;br /&gt;
| ENS210   ||  -    || -             || -   || -       || -     || 2,55 || 2,74&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [https://www.mikrocontroller.net/forum/mikrocontroller-elektronik?filter=temperatur* Beiträge im Forum]&lt;br /&gt;
* [[Feuchtesensor]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Sensorik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=107266</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=107266"/>
		<updated>2025-01-28T08:58:41Z</updated>

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

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

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

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

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

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

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

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

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

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

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

		<summary type="html">&lt;p&gt;Heha: Verweis auf DMA (für ISRs die nur Daten schaufeln)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Bei bestimmten Ereignissen in Prozessoren wird ein &#039;&#039;&#039;Interrupt&#039;&#039;&#039; (Unterbrechungsanforderung) registriert. &lt;br /&gt;
&lt;br /&gt;
Bei [[Mikrocontroller]]n werden Interrupts z.&amp;amp;nbsp;B. ausgelöst wenn:&lt;br /&gt;
* sich der an einem bestimmten Eingangs-Pin anliegende Pegel ändert&lt;br /&gt;
* eine vorher festgelegte Zeitspanne abgelaufen ist ([[Timer]])&lt;br /&gt;
* eine [[Seriell|serielle]] Übertragung abgeschlossen ist&lt;br /&gt;
* eine Messung des [[ADC |Analog-Digital-Wandlers]] abgeschlossen ist&lt;br /&gt;
&lt;br /&gt;
Die Registrierung eines Interrupts setzt ein passend zum Ereignis benanntes &#039;&#039;&#039;Interruptflag&#039;&#039;&#039; in Form eines Bits in einem speziellen Statusregister. Bei der Behandlung des Interrupts wird das Anwendungsprogramm unterbrochen, das auslösende Interruptflag gelöscht und ein Unterprogramm, die sogenannte &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine (&#039;&#039;&#039;ISR&#039;&#039;&#039;), aufgerufen. Wenn dieses beendet ist, läuft das Anwendungsprogramm ganz normal weiter.&lt;br /&gt;
&lt;br /&gt;
== Wichtige Eigenschaften von ISRs ==&lt;br /&gt;
&lt;br /&gt;
ISRs reagieren auf ein bestimmtes Ereignis, welches relativ oft oder selten passiert. Prinzipiell sollte man ISRs möglichst (zeitlich) kurz halten und schnell beenden. &lt;br /&gt;
&lt;br /&gt;
Im Mittel muss die ISR kürzer sein als die Periodendauer des Ereignisses, andernfalls wird es passieren, dass Interrupts „verschluckt“ werden, d.h. beim UART gehen Daten verloren, beim Timer gehen Zählzyklen verloren, beim AD-Wandler gehen Daten verloren usw. Solche verschluckten Interrupts sind schwer zu finden, weil es nur sehr wenige in ganz bestimmten Konstellationen sind. Wenn dann eine per Timer realisierte Uhr in der Stunde um 1 s falsch geht, merkt man das oft nicht. Langwierige Berechnungen, Auswertungen, Ausgaben oder gar Warteschleifen haben daher in ISRs nichts zu suchen. Auch typische C-Funktionen wie &amp;lt;tt&amp;gt;printf()&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;scanf()&amp;lt;/tt&amp;gt;, Ausgaben auf ein [[LCD]] usw. sollte man &amp;lt;i&amp;gt;nie&amp;lt;/i&amp;gt; in ISRs vornehmen. &lt;br /&gt;
&lt;br /&gt;
Stattdessen kommt bei Interruptbetrieb sinnvollerweise eine andere Programmiertechnik zum Einsatz, nämlich die Übergabe von Parametern bzw. Steuersignalen an das Hauptprogramm. Fachsprachlich handelt es sich um ein „bottom half“ (Linux) oder um einen „Aufgeschobenen Prozeduraufruf“ (DPC, Windows). Hierbei ist wichtig, dass die Steuerbits („Flags“), welche gemeinsam im Interrupt-Programmteil und im Nicht-Interrupt-Programmteil verwendet werden, mit dem Schlüsselwort [[Interrupt#Weblinks|volatile]] deklariert werden. Dadurch wird sichergestellt, dass jeder Zugriff auf die Variable im Code auch in die entsprechenden Maschinenbefehle umgesetzt wird und nicht wegoptimiert wird, weil sich die Variable in einem der beiden unabhängigen Programmteile scheinbar nicht ändert. Außerdem müssen sowohl der Lese- als auch Schreibzugriff auf Steuervariablen ununterbrechbar ([[Interrupt#Atomarer Datenzugriff|atomar]]) sein.&lt;br /&gt;
&lt;br /&gt;
Für die Steuerbits bietet sich die Verwendung von &amp;lt;tt&amp;gt;GPIOR0&amp;lt;/tt&amp;gt; an, welches volatil und bitadressierbar in den meisten AVR-Controllern zur Verfügung steht.&lt;br /&gt;
&lt;br /&gt;
=== Interruptsteuerung ===&lt;br /&gt;
&lt;br /&gt;
Interrupts müssen wie alle anderen Module und Funktionen eines Mikrocontrollers gesteuert werden. Dazu wird auf praktisch allen Mikrocontrollern ein zweistufiges System verwendet.&lt;br /&gt;
* Globale Interruptfreigabe über ein CPU-Statusbit: Beim [[AVR]] ist das das I-Bit (Interrupt) im Statusregister (SREG). Dieses Bit wirkt wie ein Hauptschalter und kann global die Ausführung aller Interrupts ein - und ausschalten. Das heisst aber nicht, dass während der Zeit der inaktiven Interrupts diese verloren gehen. Vielmehr wird das jeweilige Interruptbit gesetzt, und wenn die Interrupts wieder freigegeben werden wird der Interrupt ausgeführt. Verloren gehen Interrupts erst dann, wenn die Sperrzeit zu groß ist und währenddessen mehr als ein Interrupt vom selben Typ eintrifft. Siehe [[Interrupt#Zeitverhalten eines Timerinterrupts|Beispiel 1]] und [[Interrupt#Zeitverhalten des UART Empfangsinterrupts|Beispiel 2]].&lt;br /&gt;
* Lokale Interruptsteuerung für jede einzelne Interruptquelle über Maskenbits in mehreren Interruptmaskenregistern. Hier kann jede einzelne Interruptquelle individuell ein- und ausgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Dieses System hat eine Reihe von Vorteilen.&lt;br /&gt;
So können sehr schnell und einfach alle Interrupts kurzzeitig gesperrt werden, wenn beispielsweise [[Interrupt#Atomarer Datenzugriff | atomare]] Operationen durchgeführt werden sollen, oder besonders zeitkritische Abläufe ausgeführt werden. Danach können alle konfigurierten Interrupts einfach wieder freigeschaltet werden, ohne dass die CPU viele verschiedene Interruptmaskenbits verwalten müsste.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eine ISR wird demnach nur dann ausgeführt, wenn&lt;br /&gt;
* die Interrupts global freigeschaltet sind&lt;br /&gt;
* das individuelle Maskenbit gesetzt ist&lt;br /&gt;
* der Interrupt eintritt bzw. anliegt&lt;br /&gt;
&lt;br /&gt;
=== Verschachtelte Interrupts ===&lt;br /&gt;
&lt;br /&gt;
Einige Mikrocontroller, wie z.&amp;amp;nbsp;B. der [[AVR]] kennen nur zwei CPU-Zustände. Normale Programmausführung und Interruptausführung, gesteuert durch das I-Bit der CPU. Die normale Programmausführung kann jederzeit durch Interrupts unterbrochen werden. Die Interruptausführung kann nicht durch neue Interrupts unterbrochen werden. Die ISR wird erst zu Ende bearbeitet, zurück in die normale Programmausführung gesprungen und erst dann werden neue, wartende (engl. pending = anliegend) Interrupts bearbeitet.&lt;br /&gt;
&lt;br /&gt;
Etwas komplexere Mikrocontroller oder große Prozessoren bieten verschiedene Interruptlevel (Stufen) an. Dabei gilt meist je niedriger die Zahl des Levels, um so höher die Priorität. Ein Interrupt mit höherer Priorität kann einen Interrupt mit niedriger Priorität unterbrechen.&lt;br /&gt;
Ein Interrupt mit gleicher Priorität wie der gerade bearbeitete Interrupt kann das im allgemeinen nicht.&lt;br /&gt;
Das nennt man verschachtelte Interrupts (engl. nested interrupts).&lt;br /&gt;
Klassische Vertreter hierfür sind PIC18, [[8051]], PowerPC, [[X86]] und Motorola 68000.&lt;br /&gt;
&lt;br /&gt;
Auf dem AVR kann man [[AVR-GCC-Tutorial#Unterbrechbare_Interruptroutinen | verschachtelte Interrupts]] sowohl in Assembler als auch in C nachbilden, allerdings mit einigen Einschränkungen und Tücken.&lt;br /&gt;
Das ist jedoch Leuten vorbehalten, die schon viel Erfahrung auf diesem Gebiet haben.&lt;br /&gt;
Zu 99,9% braucht man sie nicht, und wenn doch sollte das Problem besser mittels DPC und einer globalen &amp;lt;tt&amp;gt;idle()&amp;lt;/tt&amp;gt;-Routine gelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Wie lange dauert meine Interruptroutine? ===&lt;br /&gt;
&lt;br /&gt;
Diese Frage sollte man beantworten können, zumindest sollte eine Worst-Case-Abschätzung gemacht werden. Das geht auf zwei Wegen.&lt;br /&gt;
&lt;br /&gt;
* Simulation, dabei muss in einer verzweigten ISR der längste Pfad simuliert werden. Dazu müssen alle beteiligten Variablen auf den ensprechenden Wert gesetzt werden.&lt;br /&gt;
* Abzählen der Takte im Assembler- oder Disassemblerlisting&lt;br /&gt;
* Messung mit dem [[Oszilloskop]], dabei wird zum Beginn der ISR ein Pin auf HIGH gesetzt und am Ende auf LOW. Damit kann man in Echtzeit die Dauer der ISR messen. Die zusätzlichen Taktzyklen zum Aufruf und verlassen der ISR sind konstant und im wesentlichen bekannt. Mit einem modernen Digitaloszilloskop und dem &amp;quot;Infinite Persistence Mode&amp;quot; kann man eine Worst-Case-Messung vornehmen&lt;br /&gt;
&lt;br /&gt;
Als Hilfsmittel zur Fehlersuche kann man auch am Ende der ISR prüfen, ob das jeweilige Interrupt-Request-Bit schon wieder gesetzt ist. Wenn ja, dann ist die ISR in den meisten Fällen zu lang. Auch hier kann man einen Ausgang auf HIGH setzen und somit den Fehler anzeigen.&lt;br /&gt;
&lt;br /&gt;
In hartnäckigen Fällen lässt der Check auf das Interrupt-Request-Bit am ISR-Ende zum wiederholten Ausführen des Interrupt-Kodes verwenden. Diese Technik nennt sich „tail chaining“ und spart den Aufwand des Register-Rettens und -Wiederherstellens, kann jedoch zur Folge haben, dass andere ISRs und das Hauptprogramm nicht mehr zum (Schach-)Zug kommen. Das nennt man „verhungern“, englisch „starvation“.&lt;br /&gt;
&lt;br /&gt;
=== Zeitverhalten eines Timerinterrupts ===&lt;br /&gt;
&lt;br /&gt;
Ein Timerinterrupt wird im allgemeinen dazu genutzt, in konstanten, periodischen Abständen bestimmte Funktionen aufzurufen. Es ist möglich, dass während eines Timerinterrupts derselbe Interrupt wieder aktiv wird, weil die Routine sehr verzweigt ist und dieses Mal sehr lange dauert.&lt;br /&gt;
&lt;br /&gt;
Wenn zum Beispiel der Timerinterrupt mit einer Periodendauer von 100ms aufgerufen wird, er aber unter bestimmten Umständen 180ms benötigt, dann wird nach 100ms nach Eintritt in die ISR der Interrupt wieder aktiv, das Timer Interrupt Flag wird gesetzt. Da aber gerade ein Interrupt bearbeitet wird, wird er nicht sofort angesprungen, weil währenddessen die Interruptfunktion global gesperrt ist (beim AVR ist das I-Bit in der CPU gelöscht). Der Interrupt wird zu Ende bearbeitet, die CPU springt zurück zum Hauptprogramm. Dabei werden die Interrupts wieder global eingeschaltet. Der zwischenzeitlich eingetroffene und zwischengespeicherte Interrupt wird nun sofort ausgeführt, sodass das Hauptprogramm praktisch gar nicht weiter kommt, bestenfalls einen Maschinenbefehl. Nun sind aber nur noch 20ms bis zum nächsten Timerinterrupt übrig. Wenn dieser nun wieder 180 ms benötigt werden in dieser Zeit aber &#039;&#039;&#039;zwei&#039;&#039;&#039; Interrupts ausgelöst, nach 20ms und 120ms. Da diese aber nicht gezählt oder andersweitig einzeln gespeichert werden können, geht ein Interrupt verloren. Das ist ein Programmfehler.&lt;br /&gt;
&lt;br /&gt;
=== Zeitverhalten des UART Empfangsinterrupts ===&lt;br /&gt;
&lt;br /&gt;
Ein [[UART]] Interrupt zum Empfang von Daten per [[RS232]] mit 115200 [[Baud]] ist ein recht häufiges Ereignis (1 Zeichen = 10 Bits = 86,8&amp;amp;mu;s). Wenn kontinuierlich Daten empfangen werden, wird nach jeweils 86,8&amp;amp;mu;s ein neuer Interrupt ausgelöst. Dabei wird das empfangene Datenbyte vom UART aus dem Empfangsschiebegregister in einem Puffer kopiert. Während das neue Zeichen Bit für Bit empfangen wird, wird es zunächst im Schieberegister des UART gespeichert. Die Daten im Puffer bleiben davon unberührt. Die CPU muss nun schnell das empfangene Datenbyte aus dem Empfangsbuffer auslesen. Die maximale Verzögerung, die sich die CPU erlauben kann von der Aktivierung des Interrupts bis zum tatsächlichen Auslesen des Datenregisters beträgt ziemlich genau die Übertragungszeit von einem Zeichen. Wenn bis dahin nicht das Zeichen von der CPU ausgelesen wurde, wird es vom UART überschrieben und ein Fehler im Statusregister des UART signalisiert (Overrun, Überlauf des Datenpuffers). Die UARTs in heutigen Mikrocontrollern haben mindestens ein Byte Puffer wie hier beschrieben. Die neueren [[AVR]]s haben sogar effektiv 3 Byte Puffer im UART, praktisch ein kleines [[FIFO]], womit der Datenempfang besser gepuffert werden kann, wenn die CPU gerade mit anderen sehr wichtigen Dingen beschäftigt ist. D.h. kurzzeitig kann sich die CPU erlauben, die Übertragungszeit von bis zu drei Zeichen zu warten, ehe sie die Daten ausliest. Dann müssen sie aber sehr schnell hintereinander gelesen werden. Im Mittel hat die CPU aber nur die Übertragungszeit &amp;lt;i&amp;gt;eines&amp;lt;/i&amp;gt; Zeichens zur Verfügung, um es abzuholen.&lt;br /&gt;
&lt;br /&gt;
Bei Mikrocontrollern mit DMA (Direct Memory Access, praktisch alle 32-Bit-Mikrocontroller) ist in so einem Fall &amp;lt;i&amp;gt;unbedingt&amp;lt;/i&amp;gt; der DMA für den Zeichenempfang zu benutzen, denn genau dafür ist er da. Das ist jedoch anfänglich schwieriger zu programmieren und erfordert ein noch tiefgreifenderes Datenblattstudium.&lt;br /&gt;
&lt;br /&gt;
=== Zusammenfassung ===&lt;br /&gt;
&lt;br /&gt;
Interruptserviceroutinen:&lt;br /&gt;
* sollten so kurz wie möglich gehalten werden&lt;br /&gt;
* können im Einzelfall nahezu doppelt so lange dauern wie die kürzeste Periodendauer des Ereignisses, ohne dass Interrupts verloren gehen (z.&amp;amp;nbsp;B. Timerinterrupt).&lt;br /&gt;
* dürfen im Mittel maximal solange dauern wie die kürzeste Periodendauer des Ereignisses&lt;br /&gt;
* dürfen maximal solange dauern, wie die kürzeste Periodendauer des Ereignisses, wenn man auf Nummer sicher gehen will, dass keine Interrupts verschluckt werden&lt;br /&gt;
* Die Interruptzeit versteht sich immer abzüglich einer kleinen Reserve für das Anspringen und Verlassen des Interrupt minus Panikreserve&lt;br /&gt;
&lt;br /&gt;
==Interruptfeste Programmierung==&lt;br /&gt;
&lt;br /&gt;
=== Atomarer Datenzugriff ===&lt;br /&gt;
&lt;br /&gt;
Von einem atomaren Zugriff (engl. atomic access) spricht man, wenn der Zugriff innerhalb einer nicht unterbrechbaren Instruktionsfolge abgearbeitet wird.&lt;br /&gt;
&lt;br /&gt;
Alle Variablen, Steuerregister und I/O-Ports, die sowohl im Hauptprogramm als auch in Interrupts verwendet werden, sind mit viel Sorgfalt zu behandeln.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  port |= 0x03;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
übersetzt sich auf AVR-Prozessoren in&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
  IN  r16,port&lt;br /&gt;
  ORI r16,0x03&lt;br /&gt;
  OUT port,r16&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn nun zwischen IN und OUT ein Interrupt auftritt, der beispielsweise Bit&amp;amp;nbsp;7 verändert, dann geht mit dem OUT-Befehl diese Änderung verloren, da der OUT-Befehl den alten Zustand vor dem Interrupt wiederherstellt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Gefährlich ist das insbesondere deshalb, weil der Fall nur selten auftritt und dieses Verhalten sehr schlecht reproduzierbar ist!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Bei verschiedenen Prozessor-Architekturen tritt das Problem verschieden häufig auf. So übersetzt sich obiger Code bei MSP430 Prozessoren in einen einzelnen Befehl&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  OR  #0x03,port&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
und stellt somit kein Problem dar. Im Zweifel hilft nur ein Blick in den erzeugten Assembler-Code. Bei der Übernahme fremden Codes ist dies zu beachten. Was beim [[8051]] kein Problem war, kann beim [[AVR]] zu einem Problem werden, unter Umständen sogar abhängig vom verwendeten Port sein (einige Ports bzw. I/O Register können mit den atomaren Befehlen sbi, cbi zum Setzen und Löschen einzelner Bits erreicht werden, andere nicht).&lt;br /&gt;
&lt;br /&gt;
Ein ähnliches Problem entsteht bei Variablen, deren Größe die Wortbreite der Maschine übersteigt, unter Umständen auch Bitfeld-Zugriffe. Bei 8-Bit-Prozessoren wie AVR oder 8051 also bereits bei normalen &amp;quot;int&amp;quot; Variablen. Diese Variablen werden zwangsläufig byteweise verarbeitet. Wenn genau dazwischen ein Interrupt erfolgt, wird ein falscher Wert gelesen. Wenn beispielsweise eine Interrupt-Routine einen 16-Bit-Zähler verwendet und von 0x00FF auf 0x0100 hochzählt, dann kann das Hauptprogramm auch schon mal versehentlich die Werte 0x01FF oder 0x0000 lesen. Ein Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* var wird in einer ISR inkrementiert */&lt;br /&gt;
volatile int var;&lt;br /&gt;
&lt;br /&gt;
int get_var (void)&lt;br /&gt;
{&lt;br /&gt;
    /* Folgende Zuweisung besteht aus mehr als einem Maschinenbefehl */&lt;br /&gt;
    return var;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Lesen von var ist in C nur ein Befehl, in Assembler werden aber mindestens 2 Befehle benötigt, da pro Maschinen-Befehl nur 8&amp;amp;nbsp;Bit bewegt werden können.&lt;br /&gt;
Die Zuweisung kann also unterbrochen werden. Das kann dazu führen, daß ein Teil alter Bits und ein Teil neuer Bits zugewiesen wird:&lt;br /&gt;
&lt;br /&gt;
# var sei 0x00FF&lt;br /&gt;
# Das Lowbyte von var wird für die return-Anweisung gelesen: 0xFF&lt;br /&gt;
# Ein Interrupt inkrementiert var um 1 auf 0x0100&lt;br /&gt;
# Das Highbyte von var wird in für die return-Anweisung gelesen: 0x01&lt;br /&gt;
# Die Funktion gibt 0x01FF zurück&lt;br /&gt;
&lt;br /&gt;
Dies ist auch ein Grund, weshalb für Programmierung auf derart maschinennaher Ebene Kenntnisse in Prozessorarchitektur und Assembler-Programmierung sehr hilfreich sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abhilfe&#039;&#039;&#039;: Wenn man sich nicht wirklich ganz sicher ist, müssen um kritische Aktivitäten herum jedesmal die Interrupts abgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel ([[AVR-GCC]]):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  // Manuelle Methode&lt;br /&gt;
  cli(); // Interrupts abschalten&lt;br /&gt;
  port |= 0x03;&lt;br /&gt;
  sei(); // Interrupts wieder einschalten&lt;br /&gt;
&lt;br /&gt;
  // Per Macros aus dem avr gcc, schöner lesbar und bequemer&lt;br /&gt;
  // siehe Doku der avr-libc, Abschnitt &amp;lt;util/atomic.h&amp;gt;&lt;br /&gt;
  ATOMIC_BLOCK(ATOMIC_FORCEON) {&lt;br /&gt;
    port |= 0x03;  &lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man ein globales Einschalten der Interrupts mit sei() vermeiden will, kann man die folgende Methode benutzen. Hierbei werden die Interrupts nur eingeschaltet, wenn sie vorher bereits eingeschaltet waren (Hinweis aus der FAQ von avr-libc):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  // ...&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t sreg_local; // Lokale Sicherungskopie von SREG&lt;br /&gt;
    sreg_local = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    // hierhin kommt der Code mit atomarem Zugriff&lt;br /&gt;
    SREG = sreg_local;&lt;br /&gt;
  } &lt;br /&gt;
&lt;br /&gt;
   // wieder per Macro&lt;br /&gt;
   ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {&lt;br /&gt;
     // hierhin kommt der Code mit atomarem Zugriff&lt;br /&gt;
   }&lt;br /&gt;
  // ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Je nach Prozessor kann man das Problem manchmal auch ohne Abschalten von Interrupts durch geeignete Programmierung lösen. So führt&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  port = (port &amp;amp; ~0x0F) | lcd_data;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
immer zum beschriebenen Problem,&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  port &amp;amp;= ~0x0F;&lt;br /&gt;
  port |= lcd_data;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
jedoch nicht, wenn die beiden Zeilen zu jeweils einem Assembler-Befehl übersetzt werden, wie z.B. auf dem MSP430, nicht jedoch auf dem AVR. Was dann aber abhängig von den Optimierungs-Einstellungen des Compilers werden kann. Eine interruptfeste Variante für AVR-Prozessoren der neuesten Generation, wie beispielsweise Tiny2313 und Mega88 (alle ab 2004):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  PINx = (PORTx &amp;amp; 0x0F) ^ lcd_data;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man diese Tricks nutzt, sollte man sie auch sehr gut und eindeutig mit einem Kommentar dokumentieren! Im Zweifelsfall sollte man lieber den soliden, eindeutigen Weg wählen und die Interrupts kurz sperren. Diese Methode ist auch gefahrlos auf andere Controller portierbar.&lt;br /&gt;
&lt;br /&gt;
=== Reentrante Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Eine Funktion ist reentrant (wiedereintrittsfest), wenn sie mehrmals gleichzeitig aktiv sein kann, ohne dass sich diese Aufrufe gegenseitig beeinflussen. Betrifft beispielsweise Funktionen, die sowohl im Hauptprogramm als auch in Interrupts aufgerufen werden. Manche C Compiler erfordern eine besondere Kennzeichnung solcher Funktionen. Wenn möglich sollte man es jedoch vermeiden, eine Funktion aus dem Hauptprogramm &#039;&#039;&#039;und&#039;&#039;&#039; aus einem Interrupt aus aufzurufen. Das ist meist problemlos machbar.&lt;br /&gt;
&lt;br /&gt;
=== Volatile Variablen ===&lt;br /&gt;
Variablen, auf die sowohl innerhalb wie auch außerhalb einer Interruptserviceroutine zugegriffen wird (schreibend oder lesend), müssen (ähnlich wie Hardwareregister) mit dem Schlüsselwort &#039;&#039;&#039;volatile&#039;&#039;&#039; (flüchtig) versehen werden, damit der C-Compiler berücksichtigen kann, dass diese Variablen jederzeit (durch das Auftreten des Interrupts) gelesen oder geschrieben werden können.&lt;br /&gt;
Ansonsten würde der C-Compiler das regelmäßige Abfragen oder Beschreiben dieser Variablen ggf. wegoptimieren, da er nicht damit rechnet, dass auf die Variable auch &amp;quot;ohne sein Zutun&amp;quot; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Eine ausführlichere Erklärung zu &amp;quot;volatile&amp;quot; ist hier zu finden: &lt;br /&gt;
[http://www.mikrocontroller.net/articles/FAQ#Was_hat_es_mit_volatile_auf_sich FAQ: Was hat es mit volatile auf sich]&lt;br /&gt;
&lt;br /&gt;
== Interrupts und Low Power Modes (Sleep) ==&lt;br /&gt;
&lt;br /&gt;
Wenn der Microcontroller in einen Low Power Mode versetzt wird, wird er durch einen Interrupt wieder aufgeweckt, z.B. in einem festen Zeitraster per Timer-Interrupt oder vom ADC nach Beendigung einer AD-Wandlung.&lt;br /&gt;
Dabei muß sichergestellt werden, daß der Interrupt erst nach dem In-Low-Power-Gehen (z.B. per Befehl &#039;&#039;sleep()&#039;&#039;) kommen kann, da sonst der µC nicht bzw. nicht rechtzeitig geweckt wird. Dazu muß eine Möglichkeit bestehen, daß der Interrupt gesperrt wird und erst mit dem Sleep-Befehl freigegeben wird. Dies scheint auf den ersten Blick unmöglich: Man kann nicht gleichzeitig zwei Befehle (&#039;&#039;sei()&#039;&#039; und &#039;&#039;sleep()&#039;&#039;) ausführen.&lt;br /&gt;
Es gibt spezielle Mechanismen für diesen Fall. Beim C51 und beim AVR ist es z.B. so, daß &#039;&#039;sei()&#039;&#039; erst einen Befehl später &amp;quot;wirksam&amp;quot; wird. Dadurch wird die Kombination &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sei(); &lt;br /&gt;
sleep();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ununterbrechbar (natürlich sofern zuvor die Interrupts gesperrt wurden).&lt;br /&gt;
Andere Microcontroller bieten andere Mechanismen, z.B. sperrt der Assembler-Befehl&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DISI&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
beim PIC24 die Interrupts für eine bestimmten Anzahl von CPU-Taktzyklen. Die CPU kann dann eine vorgegebene Anzahl an folgenden Befehlen unter Interruptsperre ausführen und der Interrupt wird automatisch wieder freigegeben.&lt;br /&gt;
&lt;br /&gt;
== (AVR-) Beispiele für die praktische Programmierung ==&lt;br /&gt;
&lt;br /&gt;
Die Beispiele sind mit WINAVR 20060421 compiliert und getestet worden. Als Mikrocontroller wird ein AVR vom Typ ATmega32 verwendet. Alle Programme wurden mit Optimierungsstufe -Os compiliert.&lt;br /&gt;
&lt;br /&gt;
=== Steuersignale zwischen ISR und Hauptprogramm ===&lt;br /&gt;
&lt;br /&gt;
In vielen Anwendungen wird ein [[Timer]] verwendet, um in regelmäßigen Abständen bestimmte Aktionen auszuführen, wie z.&amp;amp;nbsp;B. Tasten abfragen, ADC-auslesen, ein LCD auffrischen etc. Wenn viele Dinge zu erledigen sind, nebenbei aber noch andere Interrupts verwendet werden, dann ist es notwendig die Funktionsaufrufe aus dem Timerinterrupt in die Hauptschleife zu verlagern. Der Interrupt signalisiert über eine Steuervariable (engl. Flag, Flagge), dass ein neuer Timerzyklus begonnen hat. Dadurch wird der Timerinterrupt sehr kurz und die langwierigen, aber meist nicht zeitkritischen Funktionen werden als normales Programm ausgeführt. Damit kann die CPU auf andere Interrupts schnell reagieren.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auf jeden Fall, dass die Steuervariable, welche in der ISR und in der Hauptschleife verwendet wird, mit [http://www.netrino.com/Embedded-Systems/How-To/C-Volatile-Keyword &#039;&#039;&#039;volatile&#039;&#039;&#039;] deklariert wird. Ausserdem müssen sowohl der Lese- als auch Schreibzugriff auf die Steuersignale [[#Atomarer Datenzugriff | &#039;&#039;&#039;atomar&#039;&#039;&#039;]] sein. Auf dem AVR ist das mit 8-Bit-Variablen direkt möglich, für grössere Variablen müssen die Interrupts kurzzeitig gesperrt werden.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel ist sehr einfach gehalten um das Prinzip zu veranschaulichen. Ein Timer mit einer Überlaufperiodendauer von ca. 65ms stößt periodisch eine Funktion zum Togglen einer LED an, welche dadurch mit ca. 7 Hz blinkt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* Timer Interrupt Demo&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit internem 1-MHz-Oszillator&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xE1&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1-kOhm-Vorwiderstand angeschlossen werden&lt;br /&gt;
*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// globale Variablen&lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t flag;&lt;br /&gt;
 &lt;br /&gt;
int main() {&lt;br /&gt;
 &lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
 &lt;br /&gt;
// Timer2 konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    TCCR2  = (1&amp;lt;&amp;lt;CS22) | (1&amp;lt;&amp;lt;CS21); // Vorteiler 256 -&amp;gt; ~65ms Überlaufperiode&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;TOIE2);            // Timer Overflow Interrupt freischalten &lt;br /&gt;
 &lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
 &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
 &lt;br /&gt;
    while(1) {&lt;br /&gt;
        if (flag == 1) { // Neuer Timerzyklus ?&lt;br /&gt;
            flag = 0;&lt;br /&gt;
 &lt;br /&gt;
            // hier steht jetzt in Normalfall ein grosser Programmblock ;-) &lt;br /&gt;
            PORTD ^= (1 &amp;lt;&amp;lt; PD5);    // LED toggeln&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Timer2 overflow Interrupt&lt;br /&gt;
// hier wird der Hauptschleife ein neuer Timerinterrupt signalisiert&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER2_OVF_vect ) {&lt;br /&gt;
    flag = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== UART mit Interrupts ===&lt;br /&gt;
&lt;br /&gt;
Der UART ist ein oft benutztes Modul eines Mikrocontrollers. Anfänger nutzen ihn meist im sogenannten Polling Betrieb (engl. to poll, abfragen). D.h. wenn ein Zeichen empfangen werden soll, fragt eine Funktion den UART in einer Schleife ununterbrochen ab, ob Daten empfangen wurden. In dieser Zeit macht die CPU nichts anderes! Und wenn lange kein Zeichen eintrifft tut sie sehr lange nichts, sie ist praktisch blockiert! Senden verläuft ähnlich, nur dass hier die CPU vor dem Senden prüft, ob der UART ein neues Byte aufnehmen kann. D.h. während der UART selbsttätig das Zeichen sendet ist die CPU zum Warten verdammt. All diese Nachteile haben nur einen Vorteil. Die Funktionen und Mechanismen zur UART-Nutzung sind sehr einfach, klein und leicht anwendbar.&lt;br /&gt;
&lt;br /&gt;
Will man aber die CPU nicht sinnlos warten lassen, was vor allem bei niedrigeren Baudraten ziemlich lange sein kann, muss man die Interrupts nutzen. Der AVR hat gleich drei davon.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;RXC&#039;&#039;&#039; (Receive Complete): Ein Zeichen wurde empfangen.&lt;br /&gt;
* &#039;&#039;&#039;UDRE&#039;&#039;&#039; (UART Data Register Empty): Der Zwischenpuffer des Senders ist leer und kann ein neues Zeichen aufnehmen. Dieser Zwischenpuffer ist wichtig, um lückenlos auch bei hohen Baudraten senden zu können.&lt;br /&gt;
* &#039;&#039;&#039;TXC&#039;&#039;&#039; (Transmit Complete): Das aktuelle Zeichen wurde vollständig inclusive Stopbit gesendet und es liegt kein neues Datenbyte im Sendepuffer. Dieser Interrupt ist extrem nützlich für eine Halbduplexkommunikation, z.&amp;amp;nbsp;B. auf einem RS485-[[Bus]]. Hier kann man nach dem vollständigen Senden aller Bytes den Bustranceiver (z.&amp;amp;nbsp;B. MAX485) von Senden auf Empfangen umschalten, um den Bus freizugeben.&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung der Interrupts kann die CPU andere Dinge bearbeiten und muss nur kurz einen Interrupt ausführen, wenn ein Zeichen empfangen oder gesendet wurde.&lt;br /&gt;
&lt;br /&gt;
Die Kommunikation zwischen ISRs und Hauptschleife erfolgt wieder durch Flags und zwei Pufferarrays (uart_rx_buffer und uart_tx_buffer). Es gibt zwei Funktionen, eine zum Senden von Strings, eine zum Empfangen. Das Senden sowie Empfangen kann parallel erfolgen und läuft vollkommen unabhängig vom Hauptprogramm. Die Daten werden in spezielle Puffer kopiert, sodass das Hauptprogramm mit seinen Strings sofort weiterarbeiten kann. Im Beispiel ist die CPU nicht wirklich mit sinnvollen Dingen beschäftigt, zur Demonstration des Prinzips aber ausreichend.&lt;br /&gt;
&lt;br /&gt;
Um das Programm real zu nutzen braucht man ein Terminalprogramm, z.&amp;amp;nbsp;B. Hyperterminal von Windows. Dort muss nur die richtige Baudrate eingestellt werden (9600 8N1, 9600 Baud, 8 Bits, keine Parität, 1 Stopbit, keine Flusskontrolle). Ausserdem muss man im Menu Datei -&amp;gt; Eigenschaften -&amp;gt; Einstellungen -&amp;gt; ASCII Konfiguration den Punkt &amp;quot;Eingegebene Zeichen lokal ausgeben (lokales Echo)&amp;quot; aktivieren. Nun kann man beliebige Texte eintippen. Mit RETURN wird die Eingabe abgeschlossen und der AVR vermittelt den empfangenen String an das Hauptprogramm. Diese sendet ihn einfach zurück, parallel dazu wird der String gemorst per LED angezeigt. Sollte es Probleme bei der Inbetriebnahme des UART geben, so findet man hier wichtige Hinweise zur [[AVR_Checkliste#UART.2FUSART | Fehlersuche]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* UART Interrupt Demo&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit 3,6864 MHz Quarz an XTAL1/XTAL2&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xFF&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1-kOhm-Vorwiderstand angeschlossen werden&lt;br /&gt;
* An PD0/PD1 ist ein MAX232 angeschlosssen, um Daten vom PC zu empfangen/senden&lt;br /&gt;
*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
 &lt;br /&gt;
// Systemtakt in Hz, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define F_CPU 3686400L               &lt;br /&gt;
// &amp;quot;Morsedauer&amp;quot; für ein Bit in Millisekunden&lt;br /&gt;
#define BITZEIT 100     &lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
 &lt;br /&gt;
// globale Variablen für den UART&lt;br /&gt;
 &lt;br /&gt;
// Puffergrösse in Bytes, RX und TX sind gleich gross&lt;br /&gt;
#define uart_buffer_size 32             &lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t uart_rx_flag=0;            // Flag, String komplett empfangen&lt;br /&gt;
volatile uint8_t uart_tx_flag=1;            // Flag, String komplett gesendet&lt;br /&gt;
char uart_rx_buffer[uart_buffer_size];      // Empfangspuffer&lt;br /&gt;
char uart_tx_buffer[uart_buffer_size];      // Sendepuffer&lt;br /&gt;
 &lt;br /&gt;
// lange, variable Wartezeit, Einheit in Millisekunden&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for (; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// einen String senden&lt;br /&gt;
// vor Aufruf der Funktion muss man prüfen, ob uart_t_flag==1 ist&lt;br /&gt;
// nur dann kann ein neuer String gesendet werden&lt;br /&gt;
 &lt;br /&gt;
void put_string(char *daten) {&lt;br /&gt;
 &lt;br /&gt;
   if (uart_tx_flag==1) {&lt;br /&gt;
      // String daten ind en Sendepuffer kopieren&lt;br /&gt;
      strcpy(uart_tx_buffer, daten);      &lt;br /&gt;
      // Flag für &#039;Senden ist komplett&#039; löschen, &lt;br /&gt;
      uart_tx_flag = 0;                    &lt;br /&gt;
      // UDRE Interrupt einschalten, los gehts&lt;br /&gt;
      UCSRB |= (1&amp;lt;&amp;lt;UDRIE); &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// einen empfangenen String kopieren&lt;br /&gt;
// vor Aufruf der Funktion muss man prüfen, ob uart_rx_flag==1 ist&lt;br /&gt;
// anderenfalls ist der RX Buffer noch ungültig&lt;br /&gt;
 &lt;br /&gt;
void get_string(char *daten) {&lt;br /&gt;
 &lt;br /&gt;
   if (uart_rx_flag==1) {&lt;br /&gt;
      // String kopieren&lt;br /&gt;
      strcpy(daten, uart_rx_buffer);      &lt;br /&gt;
      // Flag löschen&lt;br /&gt;
      uart_rx_flag = 0;                    &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Ein Byte im RS232 Format auf eine LED ausgeben&lt;br /&gt;
 &lt;br /&gt;
void morse(uint8_t data) {&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
 &lt;br /&gt;
    // Startbit, immer 0&lt;br /&gt;
    PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);           // LED aus&lt;br /&gt;
    long_delay(BITZEIT);&lt;br /&gt;
 &lt;br /&gt;
    for(i=0; i&amp;lt;8; i++) {&lt;br /&gt;
        if (data &amp;amp; 0x01)            // Prüfe Bit #0&lt;br /&gt;
            PORTD |= (1 &amp;lt;&amp;lt; PD5);    // LED an&lt;br /&gt;
        else&lt;br /&gt;
            PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);   // LED aus      &lt;br /&gt;
        long_delay(BITZEIT);        &lt;br /&gt;
        data &amp;gt;&amp;gt;= 1;                 // nächstes Bit auf Bit #0 schieben&lt;br /&gt;
    }&lt;br /&gt;
    // Stopbit, immer 1&lt;br /&gt;
    PORTD |= (1 &amp;lt;&amp;lt; PD5);            // LED an&lt;br /&gt;
    long_delay(BITZEIT);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
int main (void) {&lt;br /&gt;
 &lt;br /&gt;
    char stringbuffer[64];  // Allgemeiner Puffer für Strings&lt;br /&gt;
    uint8_t buffer_full=0;  // noch ein Flag, aber nur in der Hauptschleife&lt;br /&gt;
    char * charpointer;     // Hilfszeiger&lt;br /&gt;
    &lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
 &lt;br /&gt;
// UART konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXCIE) | (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN); &lt;br /&gt;
 &lt;br /&gt;
// Stringbuffer initialisieren&lt;br /&gt;
 &lt;br /&gt;
    stringbuffer[0] = &#039;\n&#039;;&lt;br /&gt;
    stringbuffer[1] = &#039;\r&#039;;&lt;br /&gt;
 &lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
 &lt;br /&gt;
    sei();&lt;br /&gt;
    &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
 &lt;br /&gt;
    while(1) {&lt;br /&gt;
 &lt;br /&gt;
        // &amp;quot;Sinnvolle&amp;quot; CPU Tätigkeit &lt;br /&gt;
        PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD5);&lt;br /&gt;
        long_delay(300);&lt;br /&gt;
        PORTD |= (1&amp;lt;&amp;lt;PD5);&lt;br /&gt;
        long_delay(300);&lt;br /&gt;
    &lt;br /&gt;
        // Wurde ein kompletter String empfangen &lt;br /&gt;
        // und der Buffer ist leer?&lt;br /&gt;
        if (uart_rx_flag==1 &amp;amp;&amp;amp; buffer_full==0) {    &lt;br /&gt;
            // ja, dann String lesen, &lt;br /&gt;
            // die ersten zwei Zeichen &lt;br /&gt;
            // aber nicht überschreiben&lt;br /&gt;
            get_string(stringbuffer+2);             &lt;br /&gt;
            buffer_full=1;&lt;br /&gt;
        }&lt;br /&gt;
 &lt;br /&gt;
        // Ist letzte Stringsendung abgeschlossen &lt;br /&gt;
        // und ein neuer String verfügbar?&lt;br /&gt;
        if (uart_tx_flag==1 &amp;amp;&amp;amp; buffer_full==1) {    &lt;br /&gt;
            // Newline + Carrige return anfügen&lt;br /&gt;
            strcat(stringbuffer, &amp;quot;\n\r&amp;quot;);           &lt;br /&gt;
            put_string(stringbuffer); // zurücksenden&lt;br /&gt;
            buffer_full=0; // Buffer ist wieder verfügbar&lt;br /&gt;
            // Alle Zeichen per LED morsen&lt;br /&gt;
            charpointer = stringbuffer;&lt;br /&gt;
            while(*charpointer) morse(*charpointer++);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// UART RX complete interrupt&lt;br /&gt;
 &lt;br /&gt;
// hier werden Daten vom PC empfangen und in einem String zwischengespeichert&lt;br /&gt;
// Wird ein Stringterminator empfangen, wird ein Flag gesetzt, welches dem &lt;br /&gt;
// Hauptprogramm den kompletten Empfang signalisiert&lt;br /&gt;
 &lt;br /&gt;
ISR(USART_RXC_vect) {&lt;br /&gt;
    &lt;br /&gt;
    static uint8_t uart_rx_cnt;     // Zähler für empfangene Zeichen&lt;br /&gt;
    char data;&lt;br /&gt;
 &lt;br /&gt;
    // Daten auslesen, dadurch wird das Interruptflag gelöscht              &lt;br /&gt;
    data = UDR;&lt;br /&gt;
    &lt;br /&gt;
    // Ist Puffer frei für neue Daten? &lt;br /&gt;
    if (!uart_rx_flag) {&lt;br /&gt;
        // ja, ist Ende des Strings (RETURN) erreicht?&lt;br /&gt;
        if (data==&#039;\r&#039;) {&lt;br /&gt;
            // ja, dann String terminieren&lt;br /&gt;
            uart_rx_buffer[uart_rx_cnt]=0;              &lt;br /&gt;
            // Flag für &#039;Empfangspuffer voll&#039; setzen&lt;br /&gt;
            uart_rx_flag=1;&lt;br /&gt;
            // Zähler zurücksetzen&lt;br /&gt;
            uart_rx_cnt=0;&lt;br /&gt;
        }&lt;br /&gt;
        else if (uart_rx_cnt&amp;lt;(uart_buffer_size-1)) {     &lt;br /&gt;
            // Daten in Puffer speichern&lt;br /&gt;
            // aber durch if() Pufferüberlauf vermeiden&lt;br /&gt;
            uart_rx_buffer[uart_rx_cnt]=data;          &lt;br /&gt;
            uart_rx_cnt++; // Zähler erhöhen&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// UART TX data register empty interrupt&lt;br /&gt;
// hier werden neue Daten in das UART-Senderegister geladen&lt;br /&gt;
 &lt;br /&gt;
ISR(USART_UDRE_vect) {&lt;br /&gt;
    // Zeiger auf Sendepuffer&lt;br /&gt;
    static char* uart_tx_p = uart_tx_buffer;    &lt;br /&gt;
    char data;&lt;br /&gt;
 &lt;br /&gt;
    // zu sendendes Zeichen lesen, &lt;br /&gt;
    // Zeiger auf Sendepuffer erhöhen&lt;br /&gt;
    data = *uart_tx_p++;&lt;br /&gt;
    &lt;br /&gt;
    // Ende des nullterminierten Strings erreicht?&lt;br /&gt;
    if (data==0 ) {        &lt;br /&gt;
        UCSRB &amp;amp;= ~(1&amp;lt;&amp;lt;UDRIE);       // ja, dann UDRE Interrupt ausschalten        &lt;br /&gt;
        uart_tx_p = uart_tx_buffer; // Pointer zurücksetzen&lt;br /&gt;
        uart_tx_flag = 1;           // Flag setzen, Übertragung beeendet&lt;br /&gt;
    }&lt;br /&gt;
    else UDR = data;                // nein, Daten senden&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Atomarer Zugriff auf eine 16-Bit Variable ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Atmega8 @ 4 MHz&lt;br /&gt;
// Siehe http://www.mikrocontroller.net/topic/206455&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/atomic.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Anm. Für das Programm würde wohl eine 8-Bit Variable genügen.&lt;br /&gt;
// Mit 16-Bit kann der Sinn eines atomaren Zugriffs besser&lt;br /&gt;
// demonstriert werden &lt;br /&gt;
volatile int sekunde;&lt;br /&gt;
&lt;br /&gt;
void setup (void)&lt;br /&gt;
{&lt;br /&gt;
  TCCR0 |= ( 1&amp;lt;&amp;lt;CS02 )|( 1&amp;lt;&amp;lt;CS00 );  // counter0,Prescaler auf 1024&lt;br /&gt;
  TIMSK |= ( 1&amp;lt;&amp;lt;TOIE0 ); // enable counter0 overflow interrupt&lt;br /&gt;
  TCNT0 = 0x00;          // Counter0 auf Null setzen&lt;br /&gt;
  sei();                 // Interrupts global aktivieren&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect)&lt;br /&gt;
{&lt;br /&gt;
  sekunde++;&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); // Pin PB0 Ausgang&lt;br /&gt;
  setup();&lt;br /&gt;
  while (1) {&lt;br /&gt;
    int sekunde_kopie;&lt;br /&gt;
    ATOMIC_BLOCK(ATOMIC_FORCEON)&lt;br /&gt;
    {&lt;br /&gt;
      sekunde_kopie = sekunde; // 16-Bit Zuweisung ist nicht atomar&lt;br /&gt;
                               // deshalb ATOMIC_BLOCK&lt;br /&gt;
    }&lt;br /&gt;
    if ( sekunde_kopie &amp;gt;= 25 ) {&lt;br /&gt;
      ATOMIC_BLOCK(ATOMIC_FORCEON)&lt;br /&gt;
      {&lt;br /&gt;
        sekunde = 0;  // 16-Bit Zuweisung ist nicht atomar&lt;br /&gt;
                      // deshalb ATOMIC_BLOCK&lt;br /&gt;
      }&lt;br /&gt;
      PORTB ^= (1&amp;lt;&amp;lt;PB0); // Toggle PB0&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[ADC]]&lt;br /&gt;
* [[Timer]]&lt;br /&gt;
* [[FAQ#Timer]]&lt;br /&gt;
* [[AVR-Tutorial: Interrupts | AVR-Tutorial - Interrupts in Assembler]]&lt;br /&gt;
* [[AVR-GCC-Tutorial#Programmieren_mit_Interrupts | AVR-GCC-Tutorial - Interrupts in C]]&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/121333#1110623 Forumsbeitrag]: Minimale Pulsbreite zum Auslösen eines Pin Change Interrupts beim AVR&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/65923?goto=new#528085 Forumsbeitrag]: Schwerer Bug in AVR-GCC 4.1.1&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/513157?goto=6588881#6588720 Forumsbeitrag]: Code in den Atomic Block verschleppt&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html Atomare Operationen in der avr-libc]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/optimization.html#optim_code_reorder Problems with reordering code]&lt;br /&gt;
* [http://www.embedded.com/electronics-blogs/beginner-s-corner/4023801/Introduction-to-the-Volatile-Keyword Introduction to the &#039;&#039;&#039;Volatile&#039;&#039;&#039; Keyword] von Nigel Jones auf [http://www.embedded.com/ Embedded Systems Design]&lt;br /&gt;
* [http://blog.regehr.org/archives/28 Nine ways to break your systems code using volatile]&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Interrupt&amp;diff=107254</id>
		<title>Interrupt</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Interrupt&amp;diff=107254"/>
		<updated>2025-01-27T13:44:20Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* Wichtige Eigenschaften von ISRs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Bei bestimmten Ereignissen in Prozessoren wird ein &#039;&#039;&#039;Interrupt&#039;&#039;&#039; (Unterbrechungsanforderung) registriert. &lt;br /&gt;
&lt;br /&gt;
Bei [[Mikrocontroller]]n werden Interrupts z.&amp;amp;nbsp;B. ausgelöst wenn:&lt;br /&gt;
* sich der an einem bestimmten Eingangs-Pin anliegende Pegel ändert&lt;br /&gt;
* eine vorher festgelegte Zeitspanne abgelaufen ist ([[Timer]])&lt;br /&gt;
* eine [[Seriell|serielle]] Übertragung abgeschlossen ist&lt;br /&gt;
* eine Messung des [[ADC |Analog-Digital-Wandlers]] abgeschlossen ist&lt;br /&gt;
&lt;br /&gt;
Die Registrierung eines Interrupts setzt ein passend zum Ereignis benanntes &#039;&#039;&#039;Interruptflag&#039;&#039;&#039; in Form eines Bits in einem speziellen Statusregister. Bei der Behandlung des Interrupts wird das Anwendungsprogramm unterbrochen, das auslösende Interruptflag gelöscht und ein Unterprogramm, die sogenannte &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine (&#039;&#039;&#039;ISR&#039;&#039;&#039;), aufgerufen. Wenn dieses beendet ist, läuft das Anwendungsprogramm ganz normal weiter.&lt;br /&gt;
&lt;br /&gt;
== Wichtige Eigenschaften von ISRs ==&lt;br /&gt;
&lt;br /&gt;
ISRs reagieren auf ein bestimmtes Ereignis, welches relativ oft oder selten passiert. Prinzipiell sollte man ISRs möglichst (zeitlich) kurz halten und schnell beenden. &lt;br /&gt;
&lt;br /&gt;
Im Mittel muss die ISR kürzer sein als die Periodendauer des Ereignisses, andernfalls wird es passieren, dass Interrupts „verschluckt“ werden, d.h. beim UART gehen Daten verloren, beim Timer gehen Zählzyklen verloren, beim AD-Wandler gehen Daten verloren usw. Solche verschluckten Interrupts sind schwer zu finden, weil es nur sehr wenige in ganz bestimmten Konstellationen sind. Wenn dann eine per Timer realisierte Uhr in der Stunde um 1 s falsch geht, merkt man das oft nicht. Langwierige Berechnungen, Auswertungen, Ausgaben oder gar Warteschleifen haben daher in ISRs nichts zu suchen. Auch typische C-Funktionen wie &amp;lt;tt&amp;gt;printf()&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;scanf()&amp;lt;/tt&amp;gt;, Ausgaben auf ein [[LCD]] usw. sollte man &amp;lt;i&amp;gt;nie&amp;lt;/i&amp;gt; in ISRs vornehmen. &lt;br /&gt;
&lt;br /&gt;
Stattdessen kommt bei Interruptbetrieb sinnvollerweise eine andere Programmiertechnik zum Einsatz, nämlich die Übergabe von Parametern bzw. Steuersignalen an das Hauptprogramm. Fachsprachlich handelt es sich um ein „bottom half“ (Linux) oder um einen „Aufgeschobenen Prozeduraufruf“ (DPC, Windows). Hierbei ist wichtig, dass die Steuerbits („Flags“), welche gemeinsam im Interrupt-Programmteil und im Nicht-Interrupt-Programmteil verwendet werden, mit dem Schlüsselwort [[Interrupt#Weblinks|volatile]] deklariert werden. Dadurch wird sichergestellt, dass jeder Zugriff auf die Variable im Code auch in die entsprechenden Maschinenbefehle umgesetzt wird und nicht wegoptimiert wird, weil sich die Variable in einem der beiden unabhängigen Programmteile scheinbar nicht ändert. Außerdem müssen sowohl der Lese- als auch Schreibzugriff auf Steuervariablen ununterbrechbar ([[Interrupt#Atomarer Datenzugriff|atomar]]) sein.&lt;br /&gt;
&lt;br /&gt;
Für die Steuerbits bietet sich die Verwendung von &amp;lt;tt&amp;gt;GPIOR0&amp;lt;/tt&amp;gt; an, welches volatil und bitadressierbar in den meisten AVR-Controllern zur Verfügung steht.&lt;br /&gt;
&lt;br /&gt;
=== Interruptsteuerung ===&lt;br /&gt;
&lt;br /&gt;
Interrupts müssen wie alle anderen Module und Funktionen eines Mikrocontrollers gesteuert werden. Dazu wird auf praktisch allen Mikrocontrollern ein zweistufiges System verwendet.&lt;br /&gt;
* Globale Interruptfreigabe über ein CPU-Statusbit: Beim [[AVR]] ist das das I-Bit (Interrupt) im Statusregister (SREG). Dieses Bit wirkt wie ein Hauptschalter und kann global die Ausführung aller Interrupts ein - und ausschalten. Das heisst aber nicht, dass während der Zeit der inaktiven Interrupts diese verloren gehen. Vielmehr wird das jeweilige Interruptbit gesetzt, und wenn die Interrupts wieder freigegeben werden wird der Interrupt ausgeführt. Verloren gehen Interrupts erst dann, wenn die Sperrzeit zu groß ist und währenddessen mehr als ein Interrupt vom selben Typ eintrifft. Siehe [[Interrupt#Zeitverhalten eines Timerinterrupts|Beispiel 1]] und [[Interrupt#Zeitverhalten des UART Empfangsinterrupts|Beispiel 2]].&lt;br /&gt;
* Lokale Interruptsteuerung für jede einzelne Interruptquelle über Maskenbits in mehreren Interruptmaskenregistern. Hier kann jede einzelne Interruptquelle individuell ein- und ausgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Dieses System hat eine Reihe von Vorteilen.&lt;br /&gt;
So können sehr schnell und einfach alle Interrupts kurzzeitig gesperrt werden, wenn beispielsweise [[Interrupt#Atomarer Datenzugriff | atomare]] Operationen durchgeführt werden sollen, oder besonders zeitkritische Abläufe ausgeführt werden. Danach können alle konfigurierten Interrupts einfach wieder freigeschaltet werden, ohne dass die CPU viele verschiedene Interruptmaskenbits verwalten müsste.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eine ISR wird demnach nur dann ausgeführt, wenn&lt;br /&gt;
* die Interrupts global freigeschaltet sind&lt;br /&gt;
* das individuelle Maskenbit gesetzt ist&lt;br /&gt;
* der Interrupt eintritt bzw. anliegt&lt;br /&gt;
&lt;br /&gt;
=== Verschachtelte Interrupts ===&lt;br /&gt;
&lt;br /&gt;
Einige Mikrocontroller, wie z.&amp;amp;nbsp;B. der [[AVR]] kennen nur zwei CPU-Zustände. Normale Programmausführung und Interruptausführung, gesteuert durch das I-Bit der CPU. Die normale Programmausführung kann jederzeit durch Interrupts unterbrochen werden. Die Interruptausführung kann nicht durch neue Interrupts unterbrochen werden. Die ISR wird erst zu Ende bearbeitet, zurück in die normale Programmausführung gesprungen und erst dann werden neue, wartende (engl. pending = anliegend) Interrupts bearbeitet.&lt;br /&gt;
&lt;br /&gt;
Etwas komplexere Mikrocontroller oder große Prozessoren bieten verschiedene Interruptlevel (Stufen) an. Dabei gilt meist je niedriger die Zahl des Levels, um so höher die Priorität. Ein Interrupt mit höherer Priorität kann einen Interrupt mit niedriger Priorität unterbrechen.&lt;br /&gt;
Ein Interrupt mit gleicher Priorität wie der gerade bearbeitete Interrupt kann das im allgemeinen nicht.&lt;br /&gt;
Das nennt man verschachtelte Interrupts (engl. nested interrupts).&lt;br /&gt;
Klassische Vertreter hierfür sind PIC18, [[8051]], PowerPC, [[X86]] und Motorola 68000.&lt;br /&gt;
&lt;br /&gt;
Auf dem AVR kann man [[AVR-GCC-Tutorial#Unterbrechbare_Interruptroutinen | verschachtelte Interrupts]] sowohl in Assembler als auch in C nachbilden, allerdings mit einigen Einschränkungen und Tücken.&lt;br /&gt;
Das ist jedoch Leuten vorbehalten, die schon viel Erfahrung auf diesem Gebiet haben.&lt;br /&gt;
Zu 99,9% braucht man sie nicht, und wenn doch sollte das Problem besser mittels DPC und einer globalen &amp;lt;tt&amp;gt;idle()&amp;lt;/tt&amp;gt;-Routine gelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Wie lange dauert meine Interruptroutine? ===&lt;br /&gt;
&lt;br /&gt;
Diese Frage sollte man beantworten können, zumindest sollte eine Worst-Case-Abschätzung gemacht werden. Das geht auf zwei Wegen.&lt;br /&gt;
&lt;br /&gt;
* Simulation, dabei muss in einer verzweigten ISR der längste Pfad simuliert werden. Dazu müssen alle beteiligten Variablen auf den ensprechenden Wert gesetzt werden.&lt;br /&gt;
* Abzählen der Takte im Assembler- oder Disassemblerlisting&lt;br /&gt;
* Messung mit dem [[Oszilloskop]], dabei wird zum Beginn der ISR ein Pin auf HIGH gesetzt und am Ende auf LOW. Damit kann man in Echtzeit die Dauer der ISR messen. Die zusätzlichen Taktzyklen zum Aufruf und verlassen der ISR sind konstant und im wesentlichen bekannt. Mit einem modernen Digitaloszilloskop und dem &amp;quot;Infinite Persistence Mode&amp;quot; kann man eine Worst-Case-Messung vornehmen&lt;br /&gt;
&lt;br /&gt;
Als Hilfsmittel zur Fehlersuche kann man auch am Ende der ISR prüfen, ob das jeweilige Interrupt-Request-Bit schon wieder gesetzt ist. Wenn ja, dann ist die ISR in den meisten Fällen zu lang. Auch hier kann man einen Ausgang auf HIGH setzen und somit den Fehler anzeigen.&lt;br /&gt;
&lt;br /&gt;
In hartnäckigen Fällen lässt der Check auf das Interrupt-Request-Bit am ISR-Ende zum wiederholten Ausführen des Interrupt-Kodes verwenden. Diese Technik nennt sich „tail chaining“ und spart den Aufwand des Register-Rettens und -Wiederherstellens, kann jedoch zur Folge haben, dass andere ISRs und das Hauptprogramm nicht mehr zum (Schach-)Zug kommen. Das nennt man „verhungern“, englisch „starvation“.&lt;br /&gt;
&lt;br /&gt;
=== Zeitverhalten eines Timerinterrupts ===&lt;br /&gt;
&lt;br /&gt;
Ein Timerinterrupt wird im allgemeinen dazu genutzt, in konstanten, periodischen Abständen bestimmte Funktionen aufzurufen. Es ist möglich, dass während eines Timerinterrupts derselbe Interrupt wieder aktiv wird, weil die Routine sehr verzweigt ist und dieses Mal sehr lange dauert.&lt;br /&gt;
&lt;br /&gt;
Wenn zum Beispiel der Timerinterrupt mit einer Periodendauer von 100ms aufgerufen wird, er aber unter bestimmten Umständen 180ms benötigt, dann wird nach 100ms nach Eintritt in die ISR der Interrupt wieder aktiv, das Timer Interrupt Flag wird gesetzt. Da aber gerade ein Interrupt bearbeitet wird, wird er nicht sofort angesprungen, weil währenddessen die Interruptfunktion global gesperrt ist (beim AVR ist das I-Bit in der CPU gelöscht). Der Interrupt wird zu Ende bearbeitet, die CPU springt zurück zum Hauptprogramm. Dabei werden die Interrupts wieder global eingeschaltet. Der zwischenzeitlich eingetroffene und zwischengespeicherte Interrupt wird nun sofort ausgeführt, sodass das Hauptprogramm praktisch gar nicht weiter kommt, bestenfalls einen Maschinenbefehl. Nun sind aber nur noch 20ms bis zum nächsten Timerinterrupt übrig. Wenn dieser nun wieder 180 ms benötigt werden in dieser Zeit aber &#039;&#039;&#039;zwei&#039;&#039;&#039; Interrupts ausgelöst, nach 20ms und 120ms. Da diese aber nicht gezählt oder andersweitig einzeln gespeichert werden können, geht ein Interrupt verloren. Das ist ein Programmfehler.&lt;br /&gt;
&lt;br /&gt;
=== Zeitverhalten des UART Empfangsinterrupts ===&lt;br /&gt;
&lt;br /&gt;
Ein [[UART]] Interrupt zum Empfang von Daten per [[RS232]] mit 115200 [[Baud]] ist ein recht häufiges Ereignis (1 Zeichen = 10 Bits = 86,8&amp;amp;mu;s). Wenn kontinuierlich Daten empfangen werden, wird nach jeweils 86,8&amp;amp;mu;s ein neuer Interrupt ausgelöst. Dabei wird das empfangene Datenbyte vom UART aus dem Empfangsschiebegregister in einem Puffer kopiert. Während das neue Zeichen Bit für Bit empfangen wird, wird es zunächst im Schieberegister des UART gespeichert. Die Daten im Puffer bleiben davon unberührt. Die CPU muss nun schnell das empfangene Datenbyte aus dem Empfangsbuffer auslesen. Die maximale Verzögerung, die sich die CPU erlauben kann von der Aktivierung des Interrupts bis zum tatsächlichen Auslesen des Datenregisters beträgt ziemlich genau die Übertragungszeit von einem Zeichen. Wenn bis dahin nicht das Zeichen von der CPU ausgelesen wurde, wird es vom UART überschrieben und ein Fehler im Statusregister des UART signalisiert (Overrun, Überlauf des Datenpuffers). Die UARTs in heutigen Mikrocontrollern haben mindestens ein Byte Puffer wie hier beschrieben. Die neueren [[AVR]]s haben sogar effektiv 3 Byte Puffer im UART, praktisch ein kleines [[FIFO]], womit der Datenempfang besser gepuffert werden kann, wenn die CPU gerade mit anderen sehr wichtigen Dingen beschäftigt ist. D.h. kurzzeitig kann sich die CPU erlauben, die Übertragungszeit von bis zu drei Zeichen zu warten, ehe sie die Daten ausliest. Dann müssen sie aber sehr schnell hintereinander gelesen werden. Im Mittel hat die CPU aber nur die Übertragungszeit eines Zeichens zur Verfügung, um es abzuholen.&lt;br /&gt;
&lt;br /&gt;
=== Zusammenfassung ===&lt;br /&gt;
&lt;br /&gt;
Interruptserviceroutinen:&lt;br /&gt;
* sollten so kurz wie möglich gehalten werden&lt;br /&gt;
* können im Einzelfall nahezu doppelt so lange dauern wie die kürzeste Periodendauer des Ereignisses, ohne dass Interrupts verloren gehen (z.&amp;amp;nbsp;B. Timerinterrupt).&lt;br /&gt;
* dürfen im Mittel maximal solange dauern wie die kürzeste Periodendauer des Ereignisses&lt;br /&gt;
* dürfen maximal solange dauern, wie die kürzeste Periodendauer des Ereignisses, wenn man auf Nummer sicher gehen will, dass keine Interrupts verschluckt werden&lt;br /&gt;
* Die Interruptzeit versteht sich immer abzüglich einer kleinen Reserve für das Anspringen und Verlassen des Interrupt minus Panikreserve&lt;br /&gt;
&lt;br /&gt;
==Interruptfeste Programmierung==&lt;br /&gt;
&lt;br /&gt;
=== Atomarer Datenzugriff ===&lt;br /&gt;
&lt;br /&gt;
Von einem atomaren Zugriff (engl. atomic access) spricht man, wenn der Zugriff innerhalb einer nicht unterbrechbaren Instruktionsfolge abgearbeitet wird.&lt;br /&gt;
&lt;br /&gt;
Alle Variablen, Steuerregister und I/O-Ports, die sowohl im Hauptprogramm als auch in Interrupts verwendet werden, sind mit viel Sorgfalt zu behandeln.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  port |= 0x03;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
übersetzt sich auf AVR-Prozessoren in&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
  IN  r16,port&lt;br /&gt;
  ORI r16,0x03&lt;br /&gt;
  OUT port,r16&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn nun zwischen IN und OUT ein Interrupt auftritt, der beispielsweise Bit&amp;amp;nbsp;7 verändert, dann geht mit dem OUT-Befehl diese Änderung verloren, da der OUT-Befehl den alten Zustand vor dem Interrupt wiederherstellt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Gefährlich ist das insbesondere deshalb, weil der Fall nur selten auftritt und dieses Verhalten sehr schlecht reproduzierbar ist!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Bei verschiedenen Prozessor-Architekturen tritt das Problem verschieden häufig auf. So übersetzt sich obiger Code bei MSP430 Prozessoren in einen einzelnen Befehl&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  OR  #0x03,port&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
und stellt somit kein Problem dar. Im Zweifel hilft nur ein Blick in den erzeugten Assembler-Code. Bei der Übernahme fremden Codes ist dies zu beachten. Was beim [[8051]] kein Problem war, kann beim [[AVR]] zu einem Problem werden, unter Umständen sogar abhängig vom verwendeten Port sein (einige Ports bzw. I/O Register können mit den atomaren Befehlen sbi, cbi zum Setzen und Löschen einzelner Bits erreicht werden, andere nicht).&lt;br /&gt;
&lt;br /&gt;
Ein ähnliches Problem entsteht bei Variablen, deren Größe die Wortbreite der Maschine übersteigt, unter Umständen auch Bitfeld-Zugriffe. Bei 8-Bit-Prozessoren wie AVR oder 8051 also bereits bei normalen &amp;quot;int&amp;quot; Variablen. Diese Variablen werden zwangsläufig byteweise verarbeitet. Wenn genau dazwischen ein Interrupt erfolgt, wird ein falscher Wert gelesen. Wenn beispielsweise eine Interrupt-Routine einen 16-Bit-Zähler verwendet und von 0x00FF auf 0x0100 hochzählt, dann kann das Hauptprogramm auch schon mal versehentlich die Werte 0x01FF oder 0x0000 lesen. Ein Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* var wird in einer ISR inkrementiert */&lt;br /&gt;
volatile int var;&lt;br /&gt;
&lt;br /&gt;
int get_var (void)&lt;br /&gt;
{&lt;br /&gt;
    /* Folgende Zuweisung besteht aus mehr als einem Maschinenbefehl */&lt;br /&gt;
    return var;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Lesen von var ist in C nur ein Befehl, in Assembler werden aber mindestens 2 Befehle benötigt, da pro Maschinen-Befehl nur 8&amp;amp;nbsp;Bit bewegt werden können.&lt;br /&gt;
Die Zuweisung kann also unterbrochen werden. Das kann dazu führen, daß ein Teil alter Bits und ein Teil neuer Bits zugewiesen wird:&lt;br /&gt;
&lt;br /&gt;
# var sei 0x00FF&lt;br /&gt;
# Das Lowbyte von var wird für die return-Anweisung gelesen: 0xFF&lt;br /&gt;
# Ein Interrupt inkrementiert var um 1 auf 0x0100&lt;br /&gt;
# Das Highbyte von var wird in für die return-Anweisung gelesen: 0x01&lt;br /&gt;
# Die Funktion gibt 0x01FF zurück&lt;br /&gt;
&lt;br /&gt;
Dies ist auch ein Grund, weshalb für Programmierung auf derart maschinennaher Ebene Kenntnisse in Prozessorarchitektur und Assembler-Programmierung sehr hilfreich sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abhilfe&#039;&#039;&#039;: Wenn man sich nicht wirklich ganz sicher ist, müssen um kritische Aktivitäten herum jedesmal die Interrupts abgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel ([[AVR-GCC]]):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  // Manuelle Methode&lt;br /&gt;
  cli(); // Interrupts abschalten&lt;br /&gt;
  port |= 0x03;&lt;br /&gt;
  sei(); // Interrupts wieder einschalten&lt;br /&gt;
&lt;br /&gt;
  // Per Macros aus dem avr gcc, schöner lesbar und bequemer&lt;br /&gt;
  // siehe Doku der avr-libc, Abschnitt &amp;lt;util/atomic.h&amp;gt;&lt;br /&gt;
  ATOMIC_BLOCK(ATOMIC_FORCEON) {&lt;br /&gt;
    port |= 0x03;  &lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man ein globales Einschalten der Interrupts mit sei() vermeiden will, kann man die folgende Methode benutzen. Hierbei werden die Interrupts nur eingeschaltet, wenn sie vorher bereits eingeschaltet waren (Hinweis aus der FAQ von avr-libc):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  // ...&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t sreg_local; // Lokale Sicherungskopie von SREG&lt;br /&gt;
    sreg_local = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    // hierhin kommt der Code mit atomarem Zugriff&lt;br /&gt;
    SREG = sreg_local;&lt;br /&gt;
  } &lt;br /&gt;
&lt;br /&gt;
   // wieder per Macro&lt;br /&gt;
   ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {&lt;br /&gt;
     // hierhin kommt der Code mit atomarem Zugriff&lt;br /&gt;
   }&lt;br /&gt;
  // ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Je nach Prozessor kann man das Problem manchmal auch ohne Abschalten von Interrupts durch geeignete Programmierung lösen. So führt&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  port = (port &amp;amp; ~0x0F) | lcd_data;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
immer zum beschriebenen Problem,&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  port &amp;amp;= ~0x0F;&lt;br /&gt;
  port |= lcd_data;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
jedoch nicht, wenn die beiden Zeilen zu jeweils einem Assembler-Befehl übersetzt werden, wie z.B. auf dem MSP430, nicht jedoch auf dem AVR. Was dann aber abhängig von den Optimierungs-Einstellungen des Compilers werden kann. Eine interruptfeste Variante für AVR-Prozessoren der neuesten Generation, wie beispielsweise Tiny2313 und Mega88 (alle ab 2004):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  PINx = (PORTx &amp;amp; 0x0F) ^ lcd_data;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man diese Tricks nutzt, sollte man sie auch sehr gut und eindeutig mit einem Kommentar dokumentieren! Im Zweifelsfall sollte man lieber den soliden, eindeutigen Weg wählen und die Interrupts kurz sperren. Diese Methode ist auch gefahrlos auf andere Controller portierbar.&lt;br /&gt;
&lt;br /&gt;
=== Reentrante Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Eine Funktion ist reentrant (wiedereintrittsfest), wenn sie mehrmals gleichzeitig aktiv sein kann, ohne dass sich diese Aufrufe gegenseitig beeinflussen. Betrifft beispielsweise Funktionen, die sowohl im Hauptprogramm als auch in Interrupts aufgerufen werden. Manche C Compiler erfordern eine besondere Kennzeichnung solcher Funktionen. Wenn möglich sollte man es jedoch vermeiden, eine Funktion aus dem Hauptprogramm &#039;&#039;&#039;und&#039;&#039;&#039; aus einem Interrupt aus aufzurufen. Das ist meist problemlos machbar.&lt;br /&gt;
&lt;br /&gt;
=== Volatile Variablen ===&lt;br /&gt;
Variablen, auf die sowohl innerhalb wie auch außerhalb einer Interruptserviceroutine zugegriffen wird (schreibend oder lesend), müssen (ähnlich wie Hardwareregister) mit dem Schlüsselwort &#039;&#039;&#039;volatile&#039;&#039;&#039; (flüchtig) versehen werden, damit der C-Compiler berücksichtigen kann, dass diese Variablen jederzeit (durch das Auftreten des Interrupts) gelesen oder geschrieben werden können.&lt;br /&gt;
Ansonsten würde der C-Compiler das regelmäßige Abfragen oder Beschreiben dieser Variablen ggf. wegoptimieren, da er nicht damit rechnet, dass auf die Variable auch &amp;quot;ohne sein Zutun&amp;quot; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Eine ausführlichere Erklärung zu &amp;quot;volatile&amp;quot; ist hier zu finden: &lt;br /&gt;
[http://www.mikrocontroller.net/articles/FAQ#Was_hat_es_mit_volatile_auf_sich FAQ: Was hat es mit volatile auf sich]&lt;br /&gt;
&lt;br /&gt;
== Interrupts und Low Power Modes (Sleep) ==&lt;br /&gt;
&lt;br /&gt;
Wenn der Microcontroller in einen Low Power Mode versetzt wird, wird er durch einen Interrupt wieder aufgeweckt, z.B. in einem festen Zeitraster per Timer-Interrupt oder vom ADC nach Beendigung einer AD-Wandlung.&lt;br /&gt;
Dabei muß sichergestellt werden, daß der Interrupt erst nach dem In-Low-Power-Gehen (z.B. per Befehl &#039;&#039;sleep()&#039;&#039;) kommen kann, da sonst der µC nicht bzw. nicht rechtzeitig geweckt wird. Dazu muß eine Möglichkeit bestehen, daß der Interrupt gesperrt wird und erst mit dem Sleep-Befehl freigegeben wird. Dies scheint auf den ersten Blick unmöglich: Man kann nicht gleichzeitig zwei Befehle (&#039;&#039;sei()&#039;&#039; und &#039;&#039;sleep()&#039;&#039;) ausführen.&lt;br /&gt;
Es gibt spezielle Mechanismen für diesen Fall. Beim C51 und beim AVR ist es z.B. so, daß &#039;&#039;sei()&#039;&#039; erst einen Befehl später &amp;quot;wirksam&amp;quot; wird. Dadurch wird die Kombination &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sei(); &lt;br /&gt;
sleep();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ununterbrechbar (natürlich sofern zuvor die Interrupts gesperrt wurden).&lt;br /&gt;
Andere Microcontroller bieten andere Mechanismen, z.B. sperrt der Assembler-Befehl&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DISI&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
beim PIC24 die Interrupts für eine bestimmten Anzahl von CPU-Taktzyklen. Die CPU kann dann eine vorgegebene Anzahl an folgenden Befehlen unter Interruptsperre ausführen und der Interrupt wird automatisch wieder freigegeben.&lt;br /&gt;
&lt;br /&gt;
== (AVR-) Beispiele für die praktische Programmierung ==&lt;br /&gt;
&lt;br /&gt;
Die Beispiele sind mit WINAVR 20060421 compiliert und getestet worden. Als Mikrocontroller wird ein AVR vom Typ ATmega32 verwendet. Alle Programme wurden mit Optimierungsstufe -Os compiliert.&lt;br /&gt;
&lt;br /&gt;
=== Steuersignale zwischen ISR und Hauptprogramm ===&lt;br /&gt;
&lt;br /&gt;
In vielen Anwendungen wird ein [[Timer]] verwendet, um in regelmäßigen Abständen bestimmte Aktionen auszuführen, wie z.&amp;amp;nbsp;B. Tasten abfragen, ADC-auslesen, ein LCD auffrischen etc. Wenn viele Dinge zu erledigen sind, nebenbei aber noch andere Interrupts verwendet werden, dann ist es notwendig die Funktionsaufrufe aus dem Timerinterrupt in die Hauptschleife zu verlagern. Der Interrupt signalisiert über eine Steuervariable (engl. Flag, Flagge), dass ein neuer Timerzyklus begonnen hat. Dadurch wird der Timerinterrupt sehr kurz und die langwierigen, aber meist nicht zeitkritischen Funktionen werden als normales Programm ausgeführt. Damit kann die CPU auf andere Interrupts schnell reagieren.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auf jeden Fall, dass die Steuervariable, welche in der ISR und in der Hauptschleife verwendet wird, mit [http://www.netrino.com/Embedded-Systems/How-To/C-Volatile-Keyword &#039;&#039;&#039;volatile&#039;&#039;&#039;] deklariert wird. Ausserdem müssen sowohl der Lese- als auch Schreibzugriff auf die Steuersignale [[#Atomarer Datenzugriff | &#039;&#039;&#039;atomar&#039;&#039;&#039;]] sein. Auf dem AVR ist das mit 8-Bit-Variablen direkt möglich, für grössere Variablen müssen die Interrupts kurzzeitig gesperrt werden.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel ist sehr einfach gehalten um das Prinzip zu veranschaulichen. Ein Timer mit einer Überlaufperiodendauer von ca. 65ms stößt periodisch eine Funktion zum Togglen einer LED an, welche dadurch mit ca. 7 Hz blinkt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* Timer Interrupt Demo&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit internem 1-MHz-Oszillator&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xE1&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1-kOhm-Vorwiderstand angeschlossen werden&lt;br /&gt;
*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// globale Variablen&lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t flag;&lt;br /&gt;
 &lt;br /&gt;
int main() {&lt;br /&gt;
 &lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
 &lt;br /&gt;
// Timer2 konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    TCCR2  = (1&amp;lt;&amp;lt;CS22) | (1&amp;lt;&amp;lt;CS21); // Vorteiler 256 -&amp;gt; ~65ms Überlaufperiode&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;TOIE2);            // Timer Overflow Interrupt freischalten &lt;br /&gt;
 &lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
 &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
 &lt;br /&gt;
    while(1) {&lt;br /&gt;
        if (flag == 1) { // Neuer Timerzyklus ?&lt;br /&gt;
            flag = 0;&lt;br /&gt;
 &lt;br /&gt;
            // hier steht jetzt in Normalfall ein grosser Programmblock ;-) &lt;br /&gt;
            PORTD ^= (1 &amp;lt;&amp;lt; PD5);    // LED toggeln&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Timer2 overflow Interrupt&lt;br /&gt;
// hier wird der Hauptschleife ein neuer Timerinterrupt signalisiert&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER2_OVF_vect ) {&lt;br /&gt;
    flag = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== UART mit Interrupts ===&lt;br /&gt;
&lt;br /&gt;
Der UART ist ein oft benutztes Modul eines Mikrocontrollers. Anfänger nutzen ihn meist im sogenannten Polling Betrieb (engl. to poll, abfragen). D.h. wenn ein Zeichen empfangen werden soll, fragt eine Funktion den UART in einer Schleife ununterbrochen ab, ob Daten empfangen wurden. In dieser Zeit macht die CPU nichts anderes! Und wenn lange kein Zeichen eintrifft tut sie sehr lange nichts, sie ist praktisch blockiert! Senden verläuft ähnlich, nur dass hier die CPU vor dem Senden prüft, ob der UART ein neues Byte aufnehmen kann. D.h. während der UART selbsttätig das Zeichen sendet ist die CPU zum Warten verdammt. All diese Nachteile haben nur einen Vorteil. Die Funktionen und Mechanismen zur UART-Nutzung sind sehr einfach, klein und leicht anwendbar.&lt;br /&gt;
&lt;br /&gt;
Will man aber die CPU nicht sinnlos warten lassen, was vor allem bei niedrigeren Baudraten ziemlich lange sein kann, muss man die Interrupts nutzen. Der AVR hat gleich drei davon.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;RXC&#039;&#039;&#039; (Receive Complete): Ein Zeichen wurde empfangen.&lt;br /&gt;
* &#039;&#039;&#039;UDRE&#039;&#039;&#039; (UART Data Register Empty): Der Zwischenpuffer des Senders ist leer und kann ein neues Zeichen aufnehmen. Dieser Zwischenpuffer ist wichtig, um lückenlos auch bei hohen Baudraten senden zu können.&lt;br /&gt;
* &#039;&#039;&#039;TXC&#039;&#039;&#039; (Transmit Complete): Das aktuelle Zeichen wurde vollständig inclusive Stopbit gesendet und es liegt kein neues Datenbyte im Sendepuffer. Dieser Interrupt ist extrem nützlich für eine Halbduplexkommunikation, z.&amp;amp;nbsp;B. auf einem RS485-[[Bus]]. Hier kann man nach dem vollständigen Senden aller Bytes den Bustranceiver (z.&amp;amp;nbsp;B. MAX485) von Senden auf Empfangen umschalten, um den Bus freizugeben.&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung der Interrupts kann die CPU andere Dinge bearbeiten und muss nur kurz einen Interrupt ausführen, wenn ein Zeichen empfangen oder gesendet wurde.&lt;br /&gt;
&lt;br /&gt;
Die Kommunikation zwischen ISRs und Hauptschleife erfolgt wieder durch Flags und zwei Pufferarrays (uart_rx_buffer und uart_tx_buffer). Es gibt zwei Funktionen, eine zum Senden von Strings, eine zum Empfangen. Das Senden sowie Empfangen kann parallel erfolgen und läuft vollkommen unabhängig vom Hauptprogramm. Die Daten werden in spezielle Puffer kopiert, sodass das Hauptprogramm mit seinen Strings sofort weiterarbeiten kann. Im Beispiel ist die CPU nicht wirklich mit sinnvollen Dingen beschäftigt, zur Demonstration des Prinzips aber ausreichend.&lt;br /&gt;
&lt;br /&gt;
Um das Programm real zu nutzen braucht man ein Terminalprogramm, z.&amp;amp;nbsp;B. Hyperterminal von Windows. Dort muss nur die richtige Baudrate eingestellt werden (9600 8N1, 9600 Baud, 8 Bits, keine Parität, 1 Stopbit, keine Flusskontrolle). Ausserdem muss man im Menu Datei -&amp;gt; Eigenschaften -&amp;gt; Einstellungen -&amp;gt; ASCII Konfiguration den Punkt &amp;quot;Eingegebene Zeichen lokal ausgeben (lokales Echo)&amp;quot; aktivieren. Nun kann man beliebige Texte eintippen. Mit RETURN wird die Eingabe abgeschlossen und der AVR vermittelt den empfangenen String an das Hauptprogramm. Diese sendet ihn einfach zurück, parallel dazu wird der String gemorst per LED angezeigt. Sollte es Probleme bei der Inbetriebnahme des UART geben, so findet man hier wichtige Hinweise zur [[AVR_Checkliste#UART.2FUSART | Fehlersuche]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* UART Interrupt Demo&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit 3,6864 MHz Quarz an XTAL1/XTAL2&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xFF&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1-kOhm-Vorwiderstand angeschlossen werden&lt;br /&gt;
* An PD0/PD1 ist ein MAX232 angeschlosssen, um Daten vom PC zu empfangen/senden&lt;br /&gt;
*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
 &lt;br /&gt;
// Systemtakt in Hz, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define F_CPU 3686400L               &lt;br /&gt;
// &amp;quot;Morsedauer&amp;quot; für ein Bit in Millisekunden&lt;br /&gt;
#define BITZEIT 100     &lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
 &lt;br /&gt;
// globale Variablen für den UART&lt;br /&gt;
 &lt;br /&gt;
// Puffergrösse in Bytes, RX und TX sind gleich gross&lt;br /&gt;
#define uart_buffer_size 32             &lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t uart_rx_flag=0;            // Flag, String komplett empfangen&lt;br /&gt;
volatile uint8_t uart_tx_flag=1;            // Flag, String komplett gesendet&lt;br /&gt;
char uart_rx_buffer[uart_buffer_size];      // Empfangspuffer&lt;br /&gt;
char uart_tx_buffer[uart_buffer_size];      // Sendepuffer&lt;br /&gt;
 &lt;br /&gt;
// lange, variable Wartezeit, Einheit in Millisekunden&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for (; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// einen String senden&lt;br /&gt;
// vor Aufruf der Funktion muss man prüfen, ob uart_t_flag==1 ist&lt;br /&gt;
// nur dann kann ein neuer String gesendet werden&lt;br /&gt;
 &lt;br /&gt;
void put_string(char *daten) {&lt;br /&gt;
 &lt;br /&gt;
   if (uart_tx_flag==1) {&lt;br /&gt;
      // String daten ind en Sendepuffer kopieren&lt;br /&gt;
      strcpy(uart_tx_buffer, daten);      &lt;br /&gt;
      // Flag für &#039;Senden ist komplett&#039; löschen, &lt;br /&gt;
      uart_tx_flag = 0;                    &lt;br /&gt;
      // UDRE Interrupt einschalten, los gehts&lt;br /&gt;
      UCSRB |= (1&amp;lt;&amp;lt;UDRIE); &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// einen empfangenen String kopieren&lt;br /&gt;
// vor Aufruf der Funktion muss man prüfen, ob uart_rx_flag==1 ist&lt;br /&gt;
// anderenfalls ist der RX Buffer noch ungültig&lt;br /&gt;
 &lt;br /&gt;
void get_string(char *daten) {&lt;br /&gt;
 &lt;br /&gt;
   if (uart_rx_flag==1) {&lt;br /&gt;
      // String kopieren&lt;br /&gt;
      strcpy(daten, uart_rx_buffer);      &lt;br /&gt;
      // Flag löschen&lt;br /&gt;
      uart_rx_flag = 0;                    &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Ein Byte im RS232 Format auf eine LED ausgeben&lt;br /&gt;
 &lt;br /&gt;
void morse(uint8_t data) {&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
 &lt;br /&gt;
    // Startbit, immer 0&lt;br /&gt;
    PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);           // LED aus&lt;br /&gt;
    long_delay(BITZEIT);&lt;br /&gt;
 &lt;br /&gt;
    for(i=0; i&amp;lt;8; i++) {&lt;br /&gt;
        if (data &amp;amp; 0x01)            // Prüfe Bit #0&lt;br /&gt;
            PORTD |= (1 &amp;lt;&amp;lt; PD5);    // LED an&lt;br /&gt;
        else&lt;br /&gt;
            PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);   // LED aus      &lt;br /&gt;
        long_delay(BITZEIT);        &lt;br /&gt;
        data &amp;gt;&amp;gt;= 1;                 // nächstes Bit auf Bit #0 schieben&lt;br /&gt;
    }&lt;br /&gt;
    // Stopbit, immer 1&lt;br /&gt;
    PORTD |= (1 &amp;lt;&amp;lt; PD5);            // LED an&lt;br /&gt;
    long_delay(BITZEIT);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
int main (void) {&lt;br /&gt;
 &lt;br /&gt;
    char stringbuffer[64];  // Allgemeiner Puffer für Strings&lt;br /&gt;
    uint8_t buffer_full=0;  // noch ein Flag, aber nur in der Hauptschleife&lt;br /&gt;
    char * charpointer;     // Hilfszeiger&lt;br /&gt;
    &lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
 &lt;br /&gt;
// UART konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXCIE) | (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN); &lt;br /&gt;
 &lt;br /&gt;
// Stringbuffer initialisieren&lt;br /&gt;
 &lt;br /&gt;
    stringbuffer[0] = &#039;\n&#039;;&lt;br /&gt;
    stringbuffer[1] = &#039;\r&#039;;&lt;br /&gt;
 &lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
 &lt;br /&gt;
    sei();&lt;br /&gt;
    &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
 &lt;br /&gt;
    while(1) {&lt;br /&gt;
 &lt;br /&gt;
        // &amp;quot;Sinnvolle&amp;quot; CPU Tätigkeit &lt;br /&gt;
        PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD5);&lt;br /&gt;
        long_delay(300);&lt;br /&gt;
        PORTD |= (1&amp;lt;&amp;lt;PD5);&lt;br /&gt;
        long_delay(300);&lt;br /&gt;
    &lt;br /&gt;
        // Wurde ein kompletter String empfangen &lt;br /&gt;
        // und der Buffer ist leer?&lt;br /&gt;
        if (uart_rx_flag==1 &amp;amp;&amp;amp; buffer_full==0) {    &lt;br /&gt;
            // ja, dann String lesen, &lt;br /&gt;
            // die ersten zwei Zeichen &lt;br /&gt;
            // aber nicht überschreiben&lt;br /&gt;
            get_string(stringbuffer+2);             &lt;br /&gt;
            buffer_full=1;&lt;br /&gt;
        }&lt;br /&gt;
 &lt;br /&gt;
        // Ist letzte Stringsendung abgeschlossen &lt;br /&gt;
        // und ein neuer String verfügbar?&lt;br /&gt;
        if (uart_tx_flag==1 &amp;amp;&amp;amp; buffer_full==1) {    &lt;br /&gt;
            // Newline + Carrige return anfügen&lt;br /&gt;
            strcat(stringbuffer, &amp;quot;\n\r&amp;quot;);           &lt;br /&gt;
            put_string(stringbuffer); // zurücksenden&lt;br /&gt;
            buffer_full=0; // Buffer ist wieder verfügbar&lt;br /&gt;
            // Alle Zeichen per LED morsen&lt;br /&gt;
            charpointer = stringbuffer;&lt;br /&gt;
            while(*charpointer) morse(*charpointer++);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// UART RX complete interrupt&lt;br /&gt;
 &lt;br /&gt;
// hier werden Daten vom PC empfangen und in einem String zwischengespeichert&lt;br /&gt;
// Wird ein Stringterminator empfangen, wird ein Flag gesetzt, welches dem &lt;br /&gt;
// Hauptprogramm den kompletten Empfang signalisiert&lt;br /&gt;
 &lt;br /&gt;
ISR(USART_RXC_vect) {&lt;br /&gt;
    &lt;br /&gt;
    static uint8_t uart_rx_cnt;     // Zähler für empfangene Zeichen&lt;br /&gt;
    char data;&lt;br /&gt;
 &lt;br /&gt;
    // Daten auslesen, dadurch wird das Interruptflag gelöscht              &lt;br /&gt;
    data = UDR;&lt;br /&gt;
    &lt;br /&gt;
    // Ist Puffer frei für neue Daten? &lt;br /&gt;
    if (!uart_rx_flag) {&lt;br /&gt;
        // ja, ist Ende des Strings (RETURN) erreicht?&lt;br /&gt;
        if (data==&#039;\r&#039;) {&lt;br /&gt;
            // ja, dann String terminieren&lt;br /&gt;
            uart_rx_buffer[uart_rx_cnt]=0;              &lt;br /&gt;
            // Flag für &#039;Empfangspuffer voll&#039; setzen&lt;br /&gt;
            uart_rx_flag=1;&lt;br /&gt;
            // Zähler zurücksetzen&lt;br /&gt;
            uart_rx_cnt=0;&lt;br /&gt;
        }&lt;br /&gt;
        else if (uart_rx_cnt&amp;lt;(uart_buffer_size-1)) {     &lt;br /&gt;
            // Daten in Puffer speichern&lt;br /&gt;
            // aber durch if() Pufferüberlauf vermeiden&lt;br /&gt;
            uart_rx_buffer[uart_rx_cnt]=data;          &lt;br /&gt;
            uart_rx_cnt++; // Zähler erhöhen&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// UART TX data register empty interrupt&lt;br /&gt;
// hier werden neue Daten in das UART-Senderegister geladen&lt;br /&gt;
 &lt;br /&gt;
ISR(USART_UDRE_vect) {&lt;br /&gt;
    // Zeiger auf Sendepuffer&lt;br /&gt;
    static char* uart_tx_p = uart_tx_buffer;    &lt;br /&gt;
    char data;&lt;br /&gt;
 &lt;br /&gt;
    // zu sendendes Zeichen lesen, &lt;br /&gt;
    // Zeiger auf Sendepuffer erhöhen&lt;br /&gt;
    data = *uart_tx_p++;&lt;br /&gt;
    &lt;br /&gt;
    // Ende des nullterminierten Strings erreicht?&lt;br /&gt;
    if (data==0 ) {        &lt;br /&gt;
        UCSRB &amp;amp;= ~(1&amp;lt;&amp;lt;UDRIE);       // ja, dann UDRE Interrupt ausschalten        &lt;br /&gt;
        uart_tx_p = uart_tx_buffer; // Pointer zurücksetzen&lt;br /&gt;
        uart_tx_flag = 1;           // Flag setzen, Übertragung beeendet&lt;br /&gt;
    }&lt;br /&gt;
    else UDR = data;                // nein, Daten senden&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Atomarer Zugriff auf eine 16-Bit Variable ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Atmega8 @ 4 MHz&lt;br /&gt;
// Siehe http://www.mikrocontroller.net/topic/206455&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/atomic.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Anm. Für das Programm würde wohl eine 8-Bit Variable genügen.&lt;br /&gt;
// Mit 16-Bit kann der Sinn eines atomaren Zugriffs besser&lt;br /&gt;
// demonstriert werden &lt;br /&gt;
volatile int sekunde;&lt;br /&gt;
&lt;br /&gt;
void setup (void)&lt;br /&gt;
{&lt;br /&gt;
  TCCR0 |= ( 1&amp;lt;&amp;lt;CS02 )|( 1&amp;lt;&amp;lt;CS00 );  // counter0,Prescaler auf 1024&lt;br /&gt;
  TIMSK |= ( 1&amp;lt;&amp;lt;TOIE0 ); // enable counter0 overflow interrupt&lt;br /&gt;
  TCNT0 = 0x00;          // Counter0 auf Null setzen&lt;br /&gt;
  sei();                 // Interrupts global aktivieren&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect)&lt;br /&gt;
{&lt;br /&gt;
  sekunde++;&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); // Pin PB0 Ausgang&lt;br /&gt;
  setup();&lt;br /&gt;
  while (1) {&lt;br /&gt;
    int sekunde_kopie;&lt;br /&gt;
    ATOMIC_BLOCK(ATOMIC_FORCEON)&lt;br /&gt;
    {&lt;br /&gt;
      sekunde_kopie = sekunde; // 16-Bit Zuweisung ist nicht atomar&lt;br /&gt;
                               // deshalb ATOMIC_BLOCK&lt;br /&gt;
    }&lt;br /&gt;
    if ( sekunde_kopie &amp;gt;= 25 ) {&lt;br /&gt;
      ATOMIC_BLOCK(ATOMIC_FORCEON)&lt;br /&gt;
      {&lt;br /&gt;
        sekunde = 0;  // 16-Bit Zuweisung ist nicht atomar&lt;br /&gt;
                      // deshalb ATOMIC_BLOCK&lt;br /&gt;
      }&lt;br /&gt;
      PORTB ^= (1&amp;lt;&amp;lt;PB0); // Toggle PB0&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[ADC]]&lt;br /&gt;
* [[Timer]]&lt;br /&gt;
* [[FAQ#Timer]]&lt;br /&gt;
* [[AVR-Tutorial: Interrupts | AVR-Tutorial - Interrupts in Assembler]]&lt;br /&gt;
* [[AVR-GCC-Tutorial#Programmieren_mit_Interrupts | AVR-GCC-Tutorial - Interrupts in C]]&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/121333#1110623 Forumsbeitrag]: Minimale Pulsbreite zum Auslösen eines Pin Change Interrupts beim AVR&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/65923?goto=new#528085 Forumsbeitrag]: Schwerer Bug in AVR-GCC 4.1.1&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/513157?goto=6588881#6588720 Forumsbeitrag]: Code in den Atomic Block verschleppt&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html Atomare Operationen in der avr-libc]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/optimization.html#optim_code_reorder Problems with reordering code]&lt;br /&gt;
* [http://www.embedded.com/electronics-blogs/beginner-s-corner/4023801/Introduction-to-the-Volatile-Keyword Introduction to the &#039;&#039;&#039;Volatile&#039;&#039;&#039; Keyword] von Nigel Jones auf [http://www.embedded.com/ Embedded Systems Design]&lt;br /&gt;
* [http://blog.regehr.org/archives/28 Nine ways to break your systems code using volatile]&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Interrupts&amp;diff=107253</id>
		<title>AVR-Tutorial: Interrupts</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Interrupts&amp;diff=107253"/>
		<updated>2025-01-27T13:18:42Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* Die Interruptvektoren */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Definition==&lt;br /&gt;
&lt;br /&gt;
Bei bestimmten Ereignissen in Prozessoren wird ein sogenannter &#039;&#039;&#039;Interrupt&#039;&#039;&#039; ausgelöst. Interrupts machen es möglich, beim Eintreten eines Ereignisses sofort informiert zu werden, ohne permanent irgendeinen Status abzufragen, was teure Rechenzeit kosten würde. Dabei wird das Programm unterbrochen und ein Unterprogramm aufgerufen. Wenn dieses beendet ist, läuft das Hauptprogramm ganz normal weiter.&lt;br /&gt;
&lt;br /&gt;
==Mögliche Auslöser==&lt;br /&gt;
&lt;br /&gt;
Bei Mikrocontrollern werden Interrupts z.&amp;amp;nbsp;B. ausgelöst, wenn:&lt;br /&gt;
&lt;br /&gt;
* sich der an einem bestimmten Eingangs-Pin anliegende Wert von high auf low ändert (oder umgekehrt)&lt;br /&gt;
* eine vorher festgelegte Zeitspanne abgelaufen ist ([[Timer]])&lt;br /&gt;
* eine serielle Übertragung abgeschlossen ist ([[UART]])&lt;br /&gt;
* …&lt;br /&gt;
&lt;br /&gt;
Der ATmega8 besitzt 18 verschiedene Interruptquellen. Standardmäßig sind diese alle deaktiviert und müssen über verschiedene IO-Register einzeln eingeschaltet werden. Zusätzlich muss mit &amp;lt;tt&amp;gt;sei()&amp;lt;/tt&amp;gt; auch das I-Flag gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
==INT0, INT1 und die zugehörigen Register==&lt;br /&gt;
&lt;br /&gt;
Wir wollen uns hier ersteinmal die beiden Interrupts &#039;&#039;&#039;INT0&#039;&#039;&#039; und &#039;&#039;&#039;INT1&#039;&#039;&#039; anschauen. INT0 wird ausgelöst, wenn sich der an PD2 anliegende Wert ändert, INT1 reagiert auf Änderungen an PD3.&lt;br /&gt;
&lt;br /&gt;
Als erstes müssen wir die beiden Interrupts konfigurieren. Im Register &#039;&#039;&#039;MCUCR&#039;&#039;&#039; wird eingestellt, ob die Interrupts bei einer steigenden Flanke (low nach high) oder bei einer fallenden Flanke (high nach low) ausgelöst werden. Dafür gibt es in diesem Register die Bits &#039;&#039;&#039;ISC00&#039;&#039;&#039;, &#039;&#039;&#039;ISC01&#039;&#039;&#039; (betreffen INT0) sowie &#039;&#039;&#039;ISC10&#039;&#039;&#039; und &#039;&#039;&#039;ISC11&#039;&#039;&#039; (betreffen INT1).&lt;br /&gt;
&lt;br /&gt;
Hier eine Übersicht über die möglichen Einstellungen und was sie bewirken:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! ISC11 bzw. ISC01 || ISC10 bzw. ISC00 ||Beschreibung&lt;br /&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 am Pin löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
||Jede Änderung am Pin löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
||Eine fallende Flanke löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
||Eine steigende Flanke löst den Interrupt aus&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Danach müssen diese beiden Interrupts aktiviert werden, indem die Bits &#039;&#039;&#039;INT0&#039;&#039;&#039; und &#039;&#039;&#039;INT1&#039;&#039;&#039; im Register &#039;&#039;&#039;GICR&#039;&#039;&#039; auf &#039;&#039;&#039;1&#039;&#039;&#039; gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Die Register &#039;&#039;&#039;MCUCR&#039;&#039;&#039; und &#039;&#039;&#039;GICR&#039;&#039;&#039; gehören zwar zu den IO-Registern, können aber nicht wie andere mit den Befehlen &amp;lt;code&amp;gt;cbi&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;sbi&amp;lt;/code&amp;gt; verwendet werden. Diese Befehle wirken nur auf die IO-Register bis zur Adresse 0x1F (welches Register sich an welcher IO-Adresse befindet, steht in der Include-Datei, hier „m8def.inc“, und im Datenblatt des Controllers). Somit bleiben zum Zugriff auf diese Register nur die Befehle &amp;lt;code&amp;gt;in&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;out&amp;lt;/code&amp;gt; übrig.&lt;br /&gt;
&lt;br /&gt;
==Interrupts generell zulassen==&lt;br /&gt;
&lt;br /&gt;
Schließlich muss man noch das Ausführen von Interrupts allgemein aktivieren, was man durch einfaches Aufrufen des Assemblerbefehls &#039;&#039;&#039;&amp;lt;code&amp;gt;sei&amp;lt;/code&amp;gt;&#039;&#039;&#039; bewerkstelligt.&lt;br /&gt;
&lt;br /&gt;
==Die Interruptvektoren==&lt;br /&gt;
&lt;br /&gt;
Woher weiß der Controller jetzt, welche Routine aufgerufen werden muss, wenn ein Interrupt ausgelöst wird?&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt auftritt, dann springt die Programmausführung an eine bestimmte Stelle im Programmspeicher. Diese Stellen sind festgelegt und können nicht geändert werden:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Nr. || Adresse || Interruptname || Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x000&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  RESET&lt;br /&gt;
||Reset bzw. Einschalten der Stromversorgung&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  2&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x001&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  INT0&lt;br /&gt;
||Externer Interrupt 0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  3&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x002&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  INT1&lt;br /&gt;
||Externer Interrupt 1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  4&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x003&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER2_COMP&lt;br /&gt;
||Timer/Counter2 Compare Match&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  5&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x004&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER2_OVF&lt;br /&gt;
||Timer/Counter2 Overflow&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  6&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x005&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER1_CAPT&lt;br /&gt;
||Timer/Counter1 Capture Event&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  7&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x006&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER1_COMPA&lt;br /&gt;
||Timer/Counter1 Compare Match A&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  8&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x007&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER1_COMPB&lt;br /&gt;
||Timer/Counter1 Compare Match B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  9&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x008&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER1_OVF&lt;br /&gt;
||Timer/Counter1 Overflow&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  10&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x009&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER0_OVF&lt;br /&gt;
||Timer/Counter0 Overflow&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  11&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00A&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  SPI_STC&lt;br /&gt;
||SPI-Übertragung abgeschlossen&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  12&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00B&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  USART_RX&lt;br /&gt;
||USART-Empfang abgeschlossen&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  13&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00C&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  USART_UDRE&lt;br /&gt;
||USART-Datenregister leer&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  14&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00D&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  USART_TX&lt;br /&gt;
||USART-Sendung abgeschlossen&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  15&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00E&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  ADC&lt;br /&gt;
||AD-Wandlung abgeschlossen&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  16&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00F&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  EE_RDY&lt;br /&gt;
||EEPROM bereit&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  17&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x010&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  ANA_COMP&lt;br /&gt;
||Analogkomparator&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  18&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x011&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TWI&lt;br /&gt;
||Two-Wire Interface&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  19&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x012&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  SPM_RDY&lt;br /&gt;
||Store Program Memory Ready&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;small&amp;gt;Die Spalten „Nummer“ und „Adresse“ sind &amp;lt;u&amp;gt;so&amp;lt;/u&amp;gt; im Original-Datenblatt eher verwirrend.&lt;br /&gt;
Denn erfahrene Programmierer beginnen mit 0 zu zählen und denken in Byte-Adressen. Hier hingegen beginnt die Zählung mit 1, und es sind Word-Adressen.&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So, wir wissen jetzt, dass der Controller zu Adresse 0x001 springt, wenn INT0 auftritt. Aber dort ist ja nur Platz für einen Befehl, denn die nächste Adresse ist doch für INT1 reserviert. Wie geht das? Ganz einfach: Dort kommt ein Sprungbefehl rein, z.&amp;amp;nbsp;B. &amp;lt;code&amp;gt;rjmp interrupt0&amp;lt;/code&amp;gt;. Irgendwo anders im Programm muss in diesem Fall eine Stelle mit &amp;lt;code&amp;gt;interrupt0:&amp;lt;/code&amp;gt; gekennzeichnet sein, zu der dann gesprungen wird. Diese durch den Interrupt aufgerufene Routine nennt man &#039;&#039;Interrupthandler&#039;&#039; (engl. &amp;lt;span style=&amp;quot;text-decoration:underline&amp;quot;&amp;gt;I&amp;lt;/span&amp;gt;nterrupt &amp;lt;span style=&amp;quot;text-decoration:underline&amp;quot;&amp;gt;S&amp;lt;/span&amp;gt;ervice &amp;lt;span style=&amp;quot;text-decoration:underline&amp;quot;&amp;gt;R&amp;lt;/span&amp;gt;outine, ISR).&lt;br /&gt;
&lt;br /&gt;
==Beenden eines Interrupthandlers==&lt;br /&gt;
&lt;br /&gt;
Und wie wird die Interruptroutine wieder beendet? Durch den Befehl &#039;&#039;&#039;&amp;lt;code&amp;gt;reti&amp;lt;/code&amp;gt;&#039;&#039;&#039;. Wird dieser aufgerufen, dann wird das Programm ganz normal dort fortgesetzt, wo es durch den Interrupt unterbrochen wurde. Es ist dabei wichtig, daß hier der Befehl &amp;lt;code&amp;gt;reti&amp;lt;/code&amp;gt; und nicht ein normaler &amp;lt;code&amp;gt;ret&amp;lt;/code&amp;gt; benutzt wird. Wird ein Interrupthandler betreten, so sperrt der Mikrocontroller automatisch alle weiteren Interrupts. Im Unterschied zu &amp;lt;code&amp;gt;ret&amp;lt;/code&amp;gt; hebt ein &amp;lt;code&amp;gt;reti&amp;lt;/code&amp;gt; diese Sperre wieder auf.&lt;br /&gt;
&lt;br /&gt;
==Aufbau der Interruptvektortabelle==&lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem Assembler nur noch klarmachen, dass er unser &amp;lt;code&amp;gt;rjmp interrupt0&amp;lt;/code&amp;gt; an die richtige Stelle im Programmspeicher schreibt, nämlich an den Interruptvektor für INT0. Dazu gibt es eine Assemblerdirektive. Durch &amp;lt;code&amp;gt;.org 0x001&amp;lt;/code&amp;gt; sagt man dem Assembler, dass er die darauffolgenden Befehle ab Adresse 0x001 im Programmspeicher platzieren soll. Diese Stelle wird von INT0 angesprungen.&lt;br /&gt;
&lt;br /&gt;
Damit man nicht alle Interruptvektoren immer nachschlagen muss, sind in der Definitionsdatei m8def.inc einfach zu merkende Namen für die Adressen definiert. Statt 0x001 kann man z.&amp;amp;nbsp;B. einfach &#039;&#039;&#039;&amp;lt;code&amp;gt;INT0addr&amp;lt;/code&amp;gt;&#039;&#039;&#039; schreiben. Das hat außerdem den Vorteil, dass man bei Portierung des Programms auf einen anderen AVR-Mikrocontroller nur die passende Definitionsdatei einbinden muss, und sich über evtl. geänderte Adressen für die Interruptvektoren keine Gedanken zu machen braucht.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es nur noch ein Problem: Beim Reset (bzw. wenn die Spannung eingeschaltet wird) wird das Programm immer ab der Adresse 0x000 gestartet. Deswegen muss an diese Stelle ein Sprungbefehl zum Hauptprogramm erfolgen, z.&amp;amp;nbsp;B. &#039;&#039;&#039;&amp;lt;code&amp;gt;rjmp RESET&amp;lt;/code&amp;gt;&#039;&#039;&#039;, um an die mit &#039;&#039;&#039;&amp;lt;code&amp;gt;RESET:&amp;lt;/code&amp;gt;&#039;&#039;&#039; markierte Stelle zu springen.&lt;br /&gt;
&lt;br /&gt;
Wenn man mehrere Interrupts verwenden möchte, kann man auch, anstatt jeden Interruptvektor einzeln mit &amp;lt;code&amp;gt;.org&amp;lt;/code&amp;gt; an die richtige Stelle zu rücken, die gesamte Sprungtabelle ausschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.org 0x000                    ; kommt ganz an den Anfang des Speichers&lt;br /&gt;
         rjmp RESET           ; Interruptvektoren überspringen&lt;br /&gt;
                              ; und zum Hauptprogramm&lt;br /&gt;
         rjmp EXT_INT0        ; IRQ0 Handler&lt;br /&gt;
         rjmp EXT_INT1        ; IRQ1 Handler&lt;br /&gt;
         rjmp TIM2_COMP&lt;br /&gt;
         rjmp TIM2_OVF&lt;br /&gt;
         rjmp TIM1_CAPT       ; Timer1 Capture Handler&lt;br /&gt;
         rjmp TIM1_COMPA      ; Timer1 CompareA Handler&lt;br /&gt;
         rjmp TIM1_COMPB      ; Timer1 CompareB Handler&lt;br /&gt;
         rjmp TIM1_OVF        ; Timer1 Overflow Handler&lt;br /&gt;
         rjmp TIM0_OVF        ; Timer0 Overflow Handler&lt;br /&gt;
         rjmp SPI_STC         ; SPI Transfer Complete Handler&lt;br /&gt;
         rjmp USART_RXC       ; USART RX Complete Handler&lt;br /&gt;
         rjmp USART_DRE       ; UDR Empty Handler&lt;br /&gt;
         rjmp USART_TXC       ; USART TX Complete Handler&lt;br /&gt;
         rjmp ADC             ; ADC Conversion Complete Interrupthandler&lt;br /&gt;
         rjmp EE_RDY          ; EEPROM Ready Handler&lt;br /&gt;
         rjmp ANA_COMP        ; Analog Comparator Handler&lt;br /&gt;
         rjmp TWSI            ; Two-wire Serial Interface Handler&lt;br /&gt;
         rjmp SPM_RDY         ; Store Program Memory Ready Handler&lt;br /&gt;
&lt;br /&gt;
RESET:                        ; hier beginnt das Hauptprogramm&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier ist es unbedingt nötig, bei unbenutzten Interruptvektoren statt des Sprungbefehls den Befehl &#039;&#039;&#039;&amp;lt;code&amp;gt;reti&amp;lt;/code&amp;gt;&#039;&#039;&#039; (bzw. &amp;lt;code&amp;gt;reti nop&amp;lt;/code&amp;gt;, wenn &amp;lt;code&amp;gt;jmp&amp;lt;/code&amp;gt; 4&amp;amp;nbsp;Byte lang ist) reinzuschreiben. Wenn man einen Vektor einfach weglässt, stehen die nachfolgenden Sprungbefehle sonst alle an der falschen Adresse im Speicher.&lt;br /&gt;
&lt;br /&gt;
Wer auf Nummer sicher gehen möchte kann aber auch alle Vektoren einzeln mit &amp;lt;code&amp;gt;.org&amp;lt;/code&amp;gt; adressieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.org 0x000&lt;br /&gt;
       rjmp RESET&lt;br /&gt;
.org INT0addr                 ; External Interrupt0 Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org INT1addr                 ; External Interrupt1 Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OC2addr                  ; Output Compare2 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OVF2addr                 ; Overflow2 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org ICP1addr                 ; Input Capture1 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OC1Aaddr                 ; Output Compare1A Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OC1Baddr                 ; Output Compare1B Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OVF1addr                 ; Overflow1 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OVF0addr                 ; Overflow0 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org SPIaddr                  ; SPI Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org URXCaddr                 ; USART Receive Complete Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org UDREaddr                 ; USART Data Register Empty Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org UTXCaddr                 ; USART Transmit Complete Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org ADCCaddr                 ; ADC Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org ERDYaddr                 ; EEPROM Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org ACIaddr                  ; Analog Comparator Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org TWIaddr                  ; Irq. vector address for Two-Wire Interface&lt;br /&gt;
       reti                   &lt;br /&gt;
.org SPMRaddr                  ; SPM complete Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
&lt;br /&gt;
.org INT_VECTORS_SIZE&lt;br /&gt;
RESET:                        ; hier beginnt das Hauptprogramm&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt die unbenutzten Interruptvektoren mit &amp;lt;code&amp;gt;reti&amp;lt;/code&amp;gt; zu füllen könnte man sie hier auch einfach weglassen, da die &amp;lt;code&amp;gt;.org&amp;lt;/code&amp;gt;-Direktive dafür sorgt, dass jeder Vektor in jedem Fall am richtigen Ort im Speicher landet.&lt;br /&gt;
&lt;br /&gt;
==Beispiel==&lt;br /&gt;
So könnte ein Minimal-Assemblerprogramm aussehen, das die Interrupts INT0 und INT1 verwendet. An die Interruptpins können z.&amp;amp;nbsp;B. Taster nach bewährter Manier angeschlossen werden. Die Interrupts werden auf fallende Flanke konfiguriert, da ja die Taster so angeschlossen sind, dass sie im Ruhezustand eine 1 liefern und bei einem Tastendruck nach 0 wechseln.&lt;br /&gt;
&lt;br /&gt;
[https://www.mikrocontroller.net/sourcecode/tutorial/extinttest.asm Download extinttest.asm]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
&lt;br /&gt;
.org 0x000&lt;br /&gt;
         rjmp main            ; Reset Handler&lt;br /&gt;
.org INT0addr&lt;br /&gt;
         rjmp int0_handler    ; IRQ0 Handler&lt;br /&gt;
.org INT1addr&lt;br /&gt;
         rjmp int1_handler    ; IRQ1 Handler&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
main:                         ; hier beginnt das Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
         ldi temp, LOW(RAMEND)&lt;br /&gt;
         out SPL, temp&lt;br /&gt;
         ldi temp, HIGH(RAMEND)&lt;br /&gt;
         out SPH, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, 0x00&lt;br /&gt;
         out DDRD, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, 0xFF&lt;br /&gt;
         out DDRB, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, (1&amp;lt;&amp;lt;ISC01) | (1&amp;lt;&amp;lt;ISC11) ; INT0 und INT1 auf fallende Flanke konfigurieren&lt;br /&gt;
         out MCUCR, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, (1&amp;lt;&amp;lt;INT0) | (1&amp;lt;&amp;lt;INT1) ; INT0 und INT1 aktivieren&lt;br /&gt;
         out GICR, temp&lt;br /&gt;
&lt;br /&gt;
         sei                   ; Interrupts allgemein aktivieren&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop             ; eine leere Endlosschleife&lt;br /&gt;
&lt;br /&gt;
int0_handler:&lt;br /&gt;
         sbi PORTB, 0&lt;br /&gt;
         reti&lt;br /&gt;
&lt;br /&gt;
int1_handler:&lt;br /&gt;
         cbi PORTB, 0&lt;br /&gt;
         reti&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für dieses Programm braucht man nichts weiter als eine LED an PB0 und je einen Taster an PD2 (INT0) und PD3 (INT1). Wie diese angeschlossen werden, steht in [[AVR-Tutorial: IO-Grundlagen|Teil 2 des Tutorials]].&lt;br /&gt;
&lt;br /&gt;
Die Funktion ist auch nicht schwer zu verstehen: Drückt man eine Taste, wird der dazugehörige Interrupt aufgerufen und die LED an- oder abgeschaltet. Das ist zwar nicht sonderlich spektakulär, aber das Prinzip sollte deutlich werden.&lt;br /&gt;
&lt;br /&gt;
Meistens macht es keinen Sinn, Taster direkt an einen Interrupteingang anzuschließen. Das kann bisweilen sogar sehr schlecht sein, siehe [[Entprellung]]. Häufiger werden Interrupts in Zusammenhang mit dem UART verwendet, um z.&amp;amp;nbsp;B. auf ein empfangenes Zeichen zu reagieren. Wie das funktioniert, wird im [[AVR-Tutorial: UART|Kapitel über den UART]] beschrieben.&lt;br /&gt;
&lt;br /&gt;
==Besonderheiten des Interrupthandlers==&lt;br /&gt;
&lt;br /&gt;
Der Interrupthandler kann ja mehr oder weniger zu jedem beliebigen Zeitpunkt unabhängig vom restlichen Programm aufgerufen werden. Dabei soll das restliche Programm auf keinen Fall durch den Interrupthandler negativ beeinflusst werden, das heißt, das Hauptprogramm soll nach dem Beenden des Handlers weiterlaufen als wäre nichts passiert. Insbesondere muss deshalb darauf geachtet werden, dass im Interrupthandler Register, die vom Programmierer nicht ausschließlich nur für den Interrupthandler reserviert wurden, auf dem Stack gesichert und zum Schluss wieder hergestellt werden müssen.&lt;br /&gt;
&lt;br /&gt;
Ein Register, das gerne übersehen wird, ist das &#039;&#039;&#039;Statusregister (SREG)&#039;&#039;&#039;. In ihm merkt sich der Prozessor bestimmte Zustände von Berechnungen, z.&amp;amp;nbsp;B. ob ein arithmetischer Überlauf stattgefunden hat, ob das letzte Rechenergebnis 0 war, etc. Sobald ein Interrupthandler etwas komplizierter wird als im obigen Beispiel, tut man gut daran, das Statusregister auf jeden Fall zu sichern. Ansonsten kann das Hinzufügen von weiterem Code zum Interrupthandler schnell zum Boomerang werden: Die dann möglicherweise notwendige Sicherung des SREG wird vergessen. Überhaupt empfiehlt es sich, in diesen Dingen bei der Programmierung eines Interrupthandlers eher vorausschauend, übervorsichtig und konservativ zu programmieren. Wird dies getan, so vergeudet man höchstens ein bisschen Rechenzeit. Im anderen Fall handelt man sich allerdings einen Super-GAU ein: Man steht dann vor einem Programm, das sporadisch nicht funktioniert und keiner weiß warum. Solche Fehler sind nur sehr schwer und oft nur mit einem Quäntchen Glück zu finden.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wäre zwar das Sichern und Wiederherstellen der Register „temp“ und SREG nicht wirklich notwendig, aber hier soll die grundsätzliche Vorgehensweise gezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
&lt;br /&gt;
.org 0x000&lt;br /&gt;
         rjmp main            ; Reset Handler&lt;br /&gt;
.org INT0addr&lt;br /&gt;
         rjmp int0_handler    ; IRQ0 Handler&lt;br /&gt;
.org INT1addr&lt;br /&gt;
         rjmp int1_handler    ; IRQ1 Handler&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
main:                         ; hier beginnt das Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
         ldi temp, LOW(RAMEND)&lt;br /&gt;
         out SPL, temp&lt;br /&gt;
         ldi temp, HIGH(RAMEND)&lt;br /&gt;
         out SPH, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, 0x00&lt;br /&gt;
         out DDRD, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, 0xFF&lt;br /&gt;
         out DDRB, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, (1&amp;lt;&amp;lt;ISC01) | (1&amp;lt;&amp;lt;ISC11) ; INT0 und INT1 auf fallende Flanke konfigurieren&lt;br /&gt;
         out MCUCR, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, (1&amp;lt;&amp;lt;INT0) | (1&amp;lt;&amp;lt;INT1) ; INT0 und INT1 aktivieren&lt;br /&gt;
         out GICR, temp&lt;br /&gt;
&lt;br /&gt;
         sei                   ; Interrupts allgemein aktivieren&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop             ; eine leere Endlosschleife&lt;br /&gt;
&lt;br /&gt;
int0_handler:&lt;br /&gt;
         push temp             ; Das SREG in temp sichern. Vorher&lt;br /&gt;
         in   temp, SREG       ; muss natürlich temp gesichert werden&lt;br /&gt;
&lt;br /&gt;
         sbi PORTB, 0&lt;br /&gt;
&lt;br /&gt;
         out SREG, temp        ; Die Register SREG und temp wieder&lt;br /&gt;
         pop temp              ; herstellen&lt;br /&gt;
         reti&lt;br /&gt;
&lt;br /&gt;
int1_handler:&lt;br /&gt;
         push temp             ; Das SREG in temp sichern. Vorher&lt;br /&gt;
         in   temp, SREG       ; muss natürlich temp gesichert werden&lt;br /&gt;
&lt;br /&gt;
         cbi PORTB, 0&lt;br /&gt;
&lt;br /&gt;
         out SREG, temp        ; Die Register SREG und temp wieder&lt;br /&gt;
         pop temp              ; herstellen&lt;br /&gt;
         reti&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* Artikel [[Interrupt]]: Interrupts in C&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/353715?goto=3945222#3945222 Forumsbeitrag]: Flexible Verwaltung von Interruptroutinen ohne zentrale Interrupttabelle&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=LCD|&lt;br /&gt;
zurücklink=AVR-Tutorial: LCD|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Vergleiche|&lt;br /&gt;
vorlink=AVR-Tutorial: Vergleiche}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Interrupts]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Interrupts&amp;diff=107252</id>
		<title>AVR-Tutorial: Interrupts</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Interrupts&amp;diff=107252"/>
		<updated>2025-01-27T13:14:33Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* Mögliche Auslöser */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Definition==&lt;br /&gt;
&lt;br /&gt;
Bei bestimmten Ereignissen in Prozessoren wird ein sogenannter &#039;&#039;&#039;Interrupt&#039;&#039;&#039; ausgelöst. Interrupts machen es möglich, beim Eintreten eines Ereignisses sofort informiert zu werden, ohne permanent irgendeinen Status abzufragen, was teure Rechenzeit kosten würde. Dabei wird das Programm unterbrochen und ein Unterprogramm aufgerufen. Wenn dieses beendet ist, läuft das Hauptprogramm ganz normal weiter.&lt;br /&gt;
&lt;br /&gt;
==Mögliche Auslöser==&lt;br /&gt;
&lt;br /&gt;
Bei Mikrocontrollern werden Interrupts z.&amp;amp;nbsp;B. ausgelöst, wenn:&lt;br /&gt;
&lt;br /&gt;
* sich der an einem bestimmten Eingangs-Pin anliegende Wert von high auf low ändert (oder umgekehrt)&lt;br /&gt;
* eine vorher festgelegte Zeitspanne abgelaufen ist ([[Timer]])&lt;br /&gt;
* eine serielle Übertragung abgeschlossen ist ([[UART]])&lt;br /&gt;
* …&lt;br /&gt;
&lt;br /&gt;
Der ATmega8 besitzt 18 verschiedene Interruptquellen. Standardmäßig sind diese alle deaktiviert und müssen über verschiedene IO-Register einzeln eingeschaltet werden. Zusätzlich muss mit &amp;lt;tt&amp;gt;sei()&amp;lt;/tt&amp;gt; auch das I-Flag gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
==INT0, INT1 und die zugehörigen Register==&lt;br /&gt;
&lt;br /&gt;
Wir wollen uns hier ersteinmal die beiden Interrupts &#039;&#039;&#039;INT0&#039;&#039;&#039; und &#039;&#039;&#039;INT1&#039;&#039;&#039; anschauen. INT0 wird ausgelöst, wenn sich der an PD2 anliegende Wert ändert, INT1 reagiert auf Änderungen an PD3.&lt;br /&gt;
&lt;br /&gt;
Als erstes müssen wir die beiden Interrupts konfigurieren. Im Register &#039;&#039;&#039;MCUCR&#039;&#039;&#039; wird eingestellt, ob die Interrupts bei einer steigenden Flanke (low nach high) oder bei einer fallenden Flanke (high nach low) ausgelöst werden. Dafür gibt es in diesem Register die Bits &#039;&#039;&#039;ISC00&#039;&#039;&#039;, &#039;&#039;&#039;ISC01&#039;&#039;&#039; (betreffen INT0) sowie &#039;&#039;&#039;ISC10&#039;&#039;&#039; und &#039;&#039;&#039;ISC11&#039;&#039;&#039; (betreffen INT1).&lt;br /&gt;
&lt;br /&gt;
Hier eine Übersicht über die möglichen Einstellungen und was sie bewirken:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! ISC11 bzw. ISC01 || ISC10 bzw. ISC00 ||Beschreibung&lt;br /&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 am Pin löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
||Jede Änderung am Pin löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
||Eine fallende Flanke löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
||Eine steigende Flanke löst den Interrupt aus&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Danach müssen diese beiden Interrupts aktiviert werden, indem die Bits &#039;&#039;&#039;INT0&#039;&#039;&#039; und &#039;&#039;&#039;INT1&#039;&#039;&#039; im Register &#039;&#039;&#039;GICR&#039;&#039;&#039; auf &#039;&#039;&#039;1&#039;&#039;&#039; gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Die Register &#039;&#039;&#039;MCUCR&#039;&#039;&#039; und &#039;&#039;&#039;GICR&#039;&#039;&#039; gehören zwar zu den IO-Registern, können aber nicht wie andere mit den Befehlen &amp;lt;code&amp;gt;cbi&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;sbi&amp;lt;/code&amp;gt; verwendet werden. Diese Befehle wirken nur auf die IO-Register bis zur Adresse 0x1F (welches Register sich an welcher IO-Adresse befindet, steht in der Include-Datei, hier „m8def.inc“, und im Datenblatt des Controllers). Somit bleiben zum Zugriff auf diese Register nur die Befehle &amp;lt;code&amp;gt;in&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;out&amp;lt;/code&amp;gt; übrig.&lt;br /&gt;
&lt;br /&gt;
==Interrupts generell zulassen==&lt;br /&gt;
&lt;br /&gt;
Schließlich muss man noch das Ausführen von Interrupts allgemein aktivieren, was man durch einfaches Aufrufen des Assemblerbefehls &#039;&#039;&#039;&amp;lt;code&amp;gt;sei&amp;lt;/code&amp;gt;&#039;&#039;&#039; bewerkstelligt.&lt;br /&gt;
&lt;br /&gt;
==Die Interruptvektoren==&lt;br /&gt;
&lt;br /&gt;
Woher weiß der Controller jetzt, welche Routine aufgerufen werden muss, wenn ein Interrupt ausgelöst wird?&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt auftritt, dann springt die Programmausführung an eine bestimmte Stelle im Programmspeicher. Diese Stellen sind festgelegt und können nicht geändert werden:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Nr. || Adresse || Interruptname || Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x000&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  RESET&lt;br /&gt;
||Reset bzw. Einschalten der Stromversorgung&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  2&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x001&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  INT0&lt;br /&gt;
||Externer Interrupt 0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  3&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x002&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  INT1&lt;br /&gt;
||Externer Interrupt 1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  4&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x003&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER2_COMP&lt;br /&gt;
||Timer/Counter2 Compare Match&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  5&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x004&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER2_OVF&lt;br /&gt;
||Timer/Counter2 Overflow&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  6&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x005&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER1_CAPT&lt;br /&gt;
||Timer/Counter1 Capture Event&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  7&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x006&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER1_COMPA&lt;br /&gt;
||Timer/Counter1 Compare Match A&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  8&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x007&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER1_COMPB&lt;br /&gt;
||Timer/Counter1 Compare Match B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  9&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x008&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER1_OVF&lt;br /&gt;
||Timer/Counter1 Overflow&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  10&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x009&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER0_OVF&lt;br /&gt;
||Timer/Counter0 Overflow&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  11&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00A&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  SPI_STC&lt;br /&gt;
||SPI-Übertragung abgeschlossen&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  12&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00B&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  USART_RX&lt;br /&gt;
||USART-Empfang abgeschlossen&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  13&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00C&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  USART_UDRE&lt;br /&gt;
||USART-Datenregister leer&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  14&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00D&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  USART_TX&lt;br /&gt;
||USART-Sendung abgeschlossen&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  15&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00E&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  ADC&lt;br /&gt;
||AD-Wandlung abgeschlossen&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  16&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00F&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  EE_RDY&lt;br /&gt;
||EEPROM bereit&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  17&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x010&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  ANA_COMP&lt;br /&gt;
||Analogkomparator&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  18&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x011&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TWI&lt;br /&gt;
||Two-Wire Interface&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  19&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x012&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  SPM_RDY&lt;br /&gt;
||Store Program Memory Ready&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
So, wir wissen jetzt, dass der Controller zu Adresse 0x001 springt, wenn INT0 auftritt. Aber dort ist ja nur Platz für einen Befehl, denn die nächste Adresse ist doch für INT1 reserviert. Wie geht das? Ganz einfach: Dort kommt ein Sprungbefehl rein, z.&amp;amp;nbsp;B. &amp;lt;code&amp;gt;rjmp interrupt0&amp;lt;/code&amp;gt;. Irgendwo anders im Programm muss in diesem Fall eine Stelle mit &amp;lt;code&amp;gt;interrupt0:&amp;lt;/code&amp;gt; gekennzeichnet sein, zu der dann gesprungen wird. Diese durch den Interrupt aufgerufene Routine nennt man &#039;&#039;Interrupthandler&#039;&#039; (engl. &amp;lt;span style=&amp;quot;text-decoration:underline&amp;quot;&amp;gt;I&amp;lt;/span&amp;gt;nterrupt &amp;lt;span style=&amp;quot;text-decoration:underline&amp;quot;&amp;gt;S&amp;lt;/span&amp;gt;ervice &amp;lt;span style=&amp;quot;text-decoration:underline&amp;quot;&amp;gt;R&amp;lt;/span&amp;gt;outine, ISR).&lt;br /&gt;
&lt;br /&gt;
==Beenden eines Interrupthandlers==&lt;br /&gt;
&lt;br /&gt;
Und wie wird die Interruptroutine wieder beendet? Durch den Befehl &#039;&#039;&#039;&amp;lt;code&amp;gt;reti&amp;lt;/code&amp;gt;&#039;&#039;&#039;. Wird dieser aufgerufen, dann wird das Programm ganz normal dort fortgesetzt, wo es durch den Interrupt unterbrochen wurde. Es ist dabei wichtig, daß hier der Befehl &amp;lt;code&amp;gt;reti&amp;lt;/code&amp;gt; und nicht ein normaler &amp;lt;code&amp;gt;ret&amp;lt;/code&amp;gt; benutzt wird. Wird ein Interrupthandler betreten, so sperrt der Mikrocontroller automatisch alle weiteren Interrupts. Im Unterschied zu &amp;lt;code&amp;gt;ret&amp;lt;/code&amp;gt; hebt ein &amp;lt;code&amp;gt;reti&amp;lt;/code&amp;gt; diese Sperre wieder auf.&lt;br /&gt;
&lt;br /&gt;
==Aufbau der Interruptvektortabelle==&lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem Assembler nur noch klarmachen, dass er unser &amp;lt;code&amp;gt;rjmp interrupt0&amp;lt;/code&amp;gt; an die richtige Stelle im Programmspeicher schreibt, nämlich an den Interruptvektor für INT0. Dazu gibt es eine Assemblerdirektive. Durch &amp;lt;code&amp;gt;.org 0x001&amp;lt;/code&amp;gt; sagt man dem Assembler, dass er die darauffolgenden Befehle ab Adresse 0x001 im Programmspeicher platzieren soll. Diese Stelle wird von INT0 angesprungen.&lt;br /&gt;
&lt;br /&gt;
Damit man nicht alle Interruptvektoren immer nachschlagen muss, sind in der Definitionsdatei m8def.inc einfach zu merkende Namen für die Adressen definiert. Statt 0x001 kann man z.&amp;amp;nbsp;B. einfach &#039;&#039;&#039;&amp;lt;code&amp;gt;INT0addr&amp;lt;/code&amp;gt;&#039;&#039;&#039; schreiben. Das hat außerdem den Vorteil, dass man bei Portierung des Programms auf einen anderen AVR-Mikrocontroller nur die passende Definitionsdatei einbinden muss, und sich über evtl. geänderte Adressen für die Interruptvektoren keine Gedanken zu machen braucht.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es nur noch ein Problem: Beim Reset (bzw. wenn die Spannung eingeschaltet wird) wird das Programm immer ab der Adresse 0x000 gestartet. Deswegen muss an diese Stelle ein Sprungbefehl zum Hauptprogramm erfolgen, z.&amp;amp;nbsp;B. &#039;&#039;&#039;&amp;lt;code&amp;gt;rjmp RESET&amp;lt;/code&amp;gt;&#039;&#039;&#039;, um an die mit &#039;&#039;&#039;&amp;lt;code&amp;gt;RESET:&amp;lt;/code&amp;gt;&#039;&#039;&#039; markierte Stelle zu springen.&lt;br /&gt;
&lt;br /&gt;
Wenn man mehrere Interrupts verwenden möchte, kann man auch, anstatt jeden Interruptvektor einzeln mit &amp;lt;code&amp;gt;.org&amp;lt;/code&amp;gt; an die richtige Stelle zu rücken, die gesamte Sprungtabelle ausschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.org 0x000                    ; kommt ganz an den Anfang des Speichers&lt;br /&gt;
         rjmp RESET           ; Interruptvektoren überspringen&lt;br /&gt;
                              ; und zum Hauptprogramm&lt;br /&gt;
         rjmp EXT_INT0        ; IRQ0 Handler&lt;br /&gt;
         rjmp EXT_INT1        ; IRQ1 Handler&lt;br /&gt;
         rjmp TIM2_COMP&lt;br /&gt;
         rjmp TIM2_OVF&lt;br /&gt;
         rjmp TIM1_CAPT       ; Timer1 Capture Handler&lt;br /&gt;
         rjmp TIM1_COMPA      ; Timer1 CompareA Handler&lt;br /&gt;
         rjmp TIM1_COMPB      ; Timer1 CompareB Handler&lt;br /&gt;
         rjmp TIM1_OVF        ; Timer1 Overflow Handler&lt;br /&gt;
         rjmp TIM0_OVF        ; Timer0 Overflow Handler&lt;br /&gt;
         rjmp SPI_STC         ; SPI Transfer Complete Handler&lt;br /&gt;
         rjmp USART_RXC       ; USART RX Complete Handler&lt;br /&gt;
         rjmp USART_DRE       ; UDR Empty Handler&lt;br /&gt;
         rjmp USART_TXC       ; USART TX Complete Handler&lt;br /&gt;
         rjmp ADC             ; ADC Conversion Complete Interrupthandler&lt;br /&gt;
         rjmp EE_RDY          ; EEPROM Ready Handler&lt;br /&gt;
         rjmp ANA_COMP        ; Analog Comparator Handler&lt;br /&gt;
         rjmp TWSI            ; Two-wire Serial Interface Handler&lt;br /&gt;
         rjmp SPM_RDY         ; Store Program Memory Ready Handler&lt;br /&gt;
&lt;br /&gt;
RESET:                        ; hier beginnt das Hauptprogramm&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier ist es unbedingt nötig, bei unbenutzten Interruptvektoren statt des Sprungbefehls den Befehl &#039;&#039;&#039;&amp;lt;code&amp;gt;reti&amp;lt;/code&amp;gt;&#039;&#039;&#039; (bzw. &amp;lt;code&amp;gt;reti nop&amp;lt;/code&amp;gt;, wenn &amp;lt;code&amp;gt;jmp&amp;lt;/code&amp;gt; 4&amp;amp;nbsp;Byte lang ist) reinzuschreiben. Wenn man einen Vektor einfach weglässt, stehen die nachfolgenden Sprungbefehle sonst alle an der falschen Adresse im Speicher.&lt;br /&gt;
&lt;br /&gt;
Wer auf Nummer sicher gehen möchte kann aber auch alle Vektoren einzeln mit &amp;lt;code&amp;gt;.org&amp;lt;/code&amp;gt; adressieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.org 0x000&lt;br /&gt;
       rjmp RESET&lt;br /&gt;
.org INT0addr                 ; External Interrupt0 Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org INT1addr                 ; External Interrupt1 Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OC2addr                  ; Output Compare2 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OVF2addr                 ; Overflow2 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org ICP1addr                 ; Input Capture1 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OC1Aaddr                 ; Output Compare1A Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OC1Baddr                 ; Output Compare1B Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OVF1addr                 ; Overflow1 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OVF0addr                 ; Overflow0 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org SPIaddr                  ; SPI Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org URXCaddr                 ; USART Receive Complete Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org UDREaddr                 ; USART Data Register Empty Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org UTXCaddr                 ; USART Transmit Complete Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org ADCCaddr                 ; ADC Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org ERDYaddr                 ; EEPROM Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org ACIaddr                  ; Analog Comparator Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org TWIaddr                  ; Irq. vector address for Two-Wire Interface&lt;br /&gt;
       reti                   &lt;br /&gt;
.org SPMRaddr                  ; SPM complete Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
&lt;br /&gt;
.org INT_VECTORS_SIZE&lt;br /&gt;
RESET:                        ; hier beginnt das Hauptprogramm&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt die unbenutzten Interruptvektoren mit &amp;lt;code&amp;gt;reti&amp;lt;/code&amp;gt; zu füllen könnte man sie hier auch einfach weglassen, da die &amp;lt;code&amp;gt;.org&amp;lt;/code&amp;gt;-Direktive dafür sorgt, dass jeder Vektor in jedem Fall am richtigen Ort im Speicher landet.&lt;br /&gt;
&lt;br /&gt;
==Beispiel==&lt;br /&gt;
So könnte ein Minimal-Assemblerprogramm aussehen, das die Interrupts INT0 und INT1 verwendet. An die Interruptpins können z.&amp;amp;nbsp;B. Taster nach bewährter Manier angeschlossen werden. Die Interrupts werden auf fallende Flanke konfiguriert, da ja die Taster so angeschlossen sind, dass sie im Ruhezustand eine 1 liefern und bei einem Tastendruck nach 0 wechseln.&lt;br /&gt;
&lt;br /&gt;
[https://www.mikrocontroller.net/sourcecode/tutorial/extinttest.asm Download extinttest.asm]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
&lt;br /&gt;
.org 0x000&lt;br /&gt;
         rjmp main            ; Reset Handler&lt;br /&gt;
.org INT0addr&lt;br /&gt;
         rjmp int0_handler    ; IRQ0 Handler&lt;br /&gt;
.org INT1addr&lt;br /&gt;
         rjmp int1_handler    ; IRQ1 Handler&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
main:                         ; hier beginnt das Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
         ldi temp, LOW(RAMEND)&lt;br /&gt;
         out SPL, temp&lt;br /&gt;
         ldi temp, HIGH(RAMEND)&lt;br /&gt;
         out SPH, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, 0x00&lt;br /&gt;
         out DDRD, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, 0xFF&lt;br /&gt;
         out DDRB, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, (1&amp;lt;&amp;lt;ISC01) | (1&amp;lt;&amp;lt;ISC11) ; INT0 und INT1 auf fallende Flanke konfigurieren&lt;br /&gt;
         out MCUCR, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, (1&amp;lt;&amp;lt;INT0) | (1&amp;lt;&amp;lt;INT1) ; INT0 und INT1 aktivieren&lt;br /&gt;
         out GICR, temp&lt;br /&gt;
&lt;br /&gt;
         sei                   ; Interrupts allgemein aktivieren&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop             ; eine leere Endlosschleife&lt;br /&gt;
&lt;br /&gt;
int0_handler:&lt;br /&gt;
         sbi PORTB, 0&lt;br /&gt;
         reti&lt;br /&gt;
&lt;br /&gt;
int1_handler:&lt;br /&gt;
         cbi PORTB, 0&lt;br /&gt;
         reti&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für dieses Programm braucht man nichts weiter als eine LED an PB0 und je einen Taster an PD2 (INT0) und PD3 (INT1). Wie diese angeschlossen werden, steht in [[AVR-Tutorial: IO-Grundlagen|Teil 2 des Tutorials]].&lt;br /&gt;
&lt;br /&gt;
Die Funktion ist auch nicht schwer zu verstehen: Drückt man eine Taste, wird der dazugehörige Interrupt aufgerufen und die LED an- oder abgeschaltet. Das ist zwar nicht sonderlich spektakulär, aber das Prinzip sollte deutlich werden.&lt;br /&gt;
&lt;br /&gt;
Meistens macht es keinen Sinn, Taster direkt an einen Interrupteingang anzuschließen. Das kann bisweilen sogar sehr schlecht sein, siehe [[Entprellung]]. Häufiger werden Interrupts in Zusammenhang mit dem UART verwendet, um z.&amp;amp;nbsp;B. auf ein empfangenes Zeichen zu reagieren. Wie das funktioniert, wird im [[AVR-Tutorial: UART|Kapitel über den UART]] beschrieben.&lt;br /&gt;
&lt;br /&gt;
==Besonderheiten des Interrupthandlers==&lt;br /&gt;
&lt;br /&gt;
Der Interrupthandler kann ja mehr oder weniger zu jedem beliebigen Zeitpunkt unabhängig vom restlichen Programm aufgerufen werden. Dabei soll das restliche Programm auf keinen Fall durch den Interrupthandler negativ beeinflusst werden, das heißt, das Hauptprogramm soll nach dem Beenden des Handlers weiterlaufen als wäre nichts passiert. Insbesondere muss deshalb darauf geachtet werden, dass im Interrupthandler Register, die vom Programmierer nicht ausschließlich nur für den Interrupthandler reserviert wurden, auf dem Stack gesichert und zum Schluss wieder hergestellt werden müssen.&lt;br /&gt;
&lt;br /&gt;
Ein Register, das gerne übersehen wird, ist das &#039;&#039;&#039;Statusregister (SREG)&#039;&#039;&#039;. In ihm merkt sich der Prozessor bestimmte Zustände von Berechnungen, z.&amp;amp;nbsp;B. ob ein arithmetischer Überlauf stattgefunden hat, ob das letzte Rechenergebnis 0 war, etc. Sobald ein Interrupthandler etwas komplizierter wird als im obigen Beispiel, tut man gut daran, das Statusregister auf jeden Fall zu sichern. Ansonsten kann das Hinzufügen von weiterem Code zum Interrupthandler schnell zum Boomerang werden: Die dann möglicherweise notwendige Sicherung des SREG wird vergessen. Überhaupt empfiehlt es sich, in diesen Dingen bei der Programmierung eines Interrupthandlers eher vorausschauend, übervorsichtig und konservativ zu programmieren. Wird dies getan, so vergeudet man höchstens ein bisschen Rechenzeit. Im anderen Fall handelt man sich allerdings einen Super-GAU ein: Man steht dann vor einem Programm, das sporadisch nicht funktioniert und keiner weiß warum. Solche Fehler sind nur sehr schwer und oft nur mit einem Quäntchen Glück zu finden.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wäre zwar das Sichern und Wiederherstellen der Register „temp“ und SREG nicht wirklich notwendig, aber hier soll die grundsätzliche Vorgehensweise gezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
&lt;br /&gt;
.org 0x000&lt;br /&gt;
         rjmp main            ; Reset Handler&lt;br /&gt;
.org INT0addr&lt;br /&gt;
         rjmp int0_handler    ; IRQ0 Handler&lt;br /&gt;
.org INT1addr&lt;br /&gt;
         rjmp int1_handler    ; IRQ1 Handler&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
main:                         ; hier beginnt das Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
         ldi temp, LOW(RAMEND)&lt;br /&gt;
         out SPL, temp&lt;br /&gt;
         ldi temp, HIGH(RAMEND)&lt;br /&gt;
         out SPH, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, 0x00&lt;br /&gt;
         out DDRD, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, 0xFF&lt;br /&gt;
         out DDRB, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, (1&amp;lt;&amp;lt;ISC01) | (1&amp;lt;&amp;lt;ISC11) ; INT0 und INT1 auf fallende Flanke konfigurieren&lt;br /&gt;
         out MCUCR, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, (1&amp;lt;&amp;lt;INT0) | (1&amp;lt;&amp;lt;INT1) ; INT0 und INT1 aktivieren&lt;br /&gt;
         out GICR, temp&lt;br /&gt;
&lt;br /&gt;
         sei                   ; Interrupts allgemein aktivieren&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop             ; eine leere Endlosschleife&lt;br /&gt;
&lt;br /&gt;
int0_handler:&lt;br /&gt;
         push temp             ; Das SREG in temp sichern. Vorher&lt;br /&gt;
         in   temp, SREG       ; muss natürlich temp gesichert werden&lt;br /&gt;
&lt;br /&gt;
         sbi PORTB, 0&lt;br /&gt;
&lt;br /&gt;
         out SREG, temp        ; Die Register SREG und temp wieder&lt;br /&gt;
         pop temp              ; herstellen&lt;br /&gt;
         reti&lt;br /&gt;
&lt;br /&gt;
int1_handler:&lt;br /&gt;
         push temp             ; Das SREG in temp sichern. Vorher&lt;br /&gt;
         in   temp, SREG       ; muss natürlich temp gesichert werden&lt;br /&gt;
&lt;br /&gt;
         cbi PORTB, 0&lt;br /&gt;
&lt;br /&gt;
         out SREG, temp        ; Die Register SREG und temp wieder&lt;br /&gt;
         pop temp              ; herstellen&lt;br /&gt;
         reti&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* Artikel [[Interrupt]]: Interrupts in C&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/353715?goto=3945222#3945222 Forumsbeitrag]: Flexible Verwaltung von Interruptroutinen ohne zentrale Interrupttabelle&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=LCD|&lt;br /&gt;
zurücklink=AVR-Tutorial: LCD|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Vergleiche|&lt;br /&gt;
vorlink=AVR-Tutorial: Vergleiche}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Interrupts]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=107251</id>
		<title>AVR-Tutorial: Vergleiche</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=107251"/>
		<updated>2025-01-27T13:10:43Z</updated>

		<summary type="html">&lt;p&gt;Heha: Zweierkomplement&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Vergleiche und Entscheidungen sind in jeder Programmiersprache ein zentrales Mittel, um den Programmfluss abhängig von Bedingungen zu kontrollieren. In einem [[AVR]] spielen dazu vier Komponenten zusammen:&lt;br /&gt;
* Vergleichsbefehle,&lt;br /&gt;
* die Flags im Statusregister,&lt;br /&gt;
* bedingte Sprungbefehle,&lt;br /&gt;
* andere Befehle, die die Flags im Statusregister beeinflussen, wie z.&amp;amp;nbsp;B. die meisten arithmetischen Funktionen.&lt;br /&gt;
&lt;br /&gt;
Der Zusammenhang ist dabei folgender: Die Vergleichsbefehle führen einen Vergleich durch, zum Beispiel zwischen zwei Registern oder zwischen einem Register und einer Konstanten. Das Ergebnis des Vergleiches wird in den Flags abgelegt. Die bedingten Sprungbefehle werten die Flags aus und führen bei einem positiven Ergebnis den Sprung aus. Besonders der erste Satzteil ist wichtig! Den bedingten Sprungbefehlen ist es nämlich völlig egal, ob die Flags über Vergleichsbefehle oder über sonstige Befehle gesetzt wurden. Die Sprungbefehle werten einfach nur die Flags aus, wie auch immer diese zu ihrem Zustand kommen.&lt;br /&gt;
&lt;br /&gt;
==Flags==&lt;br /&gt;
&lt;br /&gt;
Die Flags sind Bits im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;. Ihre Aufgabe ist es, das Auftreten bestimmter Ereignisse, die während Berechnungen eintreten können, festzuhalten.&lt;br /&gt;
Speicherbefehle (LD, LDI, ST, MOV, …) haben auf dem AVR grundsätzlich keinen Einfluss auf das Statusregister. Will man den Inhalt eines Registers explizit testen (z.&amp;amp;nbsp;B. nach dem Laden aus dem SRAM), so kann man hierfür den TST-Befehl verwenden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | I | T | H | S | V | N | Z | C |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{{Byte|Bits im SREG| I|T|H|S|V|N|Z|C}}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Carry (C)===&lt;br /&gt;
Das Carry-Flag hält fest, ob es bei der letzten Berechnung einen Über- oder Unterlauf gab. Aber Achtung: Nicht alle arithmetischen Befehle verändern tatsächlich das Carry-Flag. So haben z.&amp;amp;nbsp;B. die Inkrementier- und Dekrementierbefehle keine Auswirkung auf dieses Flag.&lt;br /&gt;
&lt;br /&gt;
Weiterhin dient das C-Flag als „neuntes Bit“ für Schiebeoperationen und die Befehle &amp;lt;tt&amp;gt;adc&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sbc&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sbic&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;cpc&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Zero (Z)===&lt;br /&gt;
Das Zero-Flag hält fest, ob das Ergebnis der letzten 8-Bit-Berechnung gleich 0 war oder nicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Achtung:&amp;lt;/b&amp;gt; Einige AVR-Befehle „kaskadieren“ das Z-Flag: Arithmetische Operationen mit C-Flag als Input, vor allem &amp;lt;tt&amp;gt;cpc&amp;lt;/tt&amp;gt; (compare with carry). Damit eignet es sich zur Nullfeststellung für beliebig breite Zahlen. IMHO keine weitere Prozessorarchitektur tut das.&lt;br /&gt;
&lt;br /&gt;
===Negative (N)===&lt;br /&gt;
Spiegelt den Zustand des höchstwertigen Bits (Bit 7) der letzten 8-Bit-Berechnung wider. In Zweierkomplement-Arithmetik — Standard bei allen Compilern für Ganzzahlen — bedeutet ein gesetztes Bit 7 eine negative Zahl. Das Bit kann also dazu genutzt werden um festzustellen, ob das Ergebnis einer Berechnung im Sinne einer Zweierkomplement-Arithmetik positiv oder negativ ist.&lt;br /&gt;
&lt;br /&gt;
===Overflow (V)===&lt;br /&gt;
Dieses Bit wird gesetzt, wenn bei einer Berechnung mit Zweierkomplement-Arithmetik (Standard für vorzeichenbehaftete Ganzzahlen bei jedem Compiler) ein Überlauf (Unterlauf) stattgefunden hat. Dies entspricht einem Überlauf von Bit 6 ins Bit 7.&lt;br /&gt;
&lt;br /&gt;
Der Übertrag, der bei der Addition/Subtraktion von Bit 6 auf Bit 7 auftritt, zeigt daher — wenn er vorhanden ist — an,&lt;br /&gt;
dass es sich hier um einen Überlauf (Overflow) des Zahlenbereichs handelt und das Ergebnis falsch ist.&lt;br /&gt;
Das ist allerdings nicht der Fall, wenn auch der Übertrag von Bit 7 nach Bit 8 (Carry) aufgetreten ist.&lt;br /&gt;
Daher ist das Overflow-Flag die [[AVR-Tutorial: Logik#XOR (Exklusives Oder)|XOR-Verknüpfung]] aus dem Übertrag von Bit 6 nach Bit 7 und dem Carry.&lt;br /&gt;
&lt;br /&gt;
Beispiele für die Anwendung des V-Flags finden sich in [[AVR Arithmetik/Saturierung|saturierter Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Merke:&amp;lt;/b&amp;gt; Das V-Flag ist für &amp;lt;u&amp;gt;vorzeichenbehaftete&amp;lt;/u&amp;gt; Zahlen. Das C-Flag ist für &amp;lt;u&amp;gt;vorzeichenlose&amp;lt;/u&amp;gt; Zahlen auszuwerten. Eine &amp;lt;i&amp;gt;gemischte Arithmetik&amp;lt;/i&amp;gt; (vzb. + vzl.) ist hingegen &amp;lt;i&amp;gt;sehr schwierig&amp;lt;/i&amp;gt; und „zu Fuß“ auf Überläufe zu prüfen! Im Compiler behilft man sich mit einem Cast zum nächstgrößeren Datentyp.&lt;br /&gt;
&lt;br /&gt;
===Signed (S)===&lt;br /&gt;
Das Signed-Bit ergibt sich aus der Antivalenz (exklusives Oder) der Flags N und V, also S = N XOR V.&lt;br /&gt;
Mit Hilfe des Signed-Flags können vorzeichenbehaftete Werte miteinander verglichen werden. Ist nach einem Vergleich zweier Register S=1, so ist der Wert des ersten Registers kleiner dem zweiten (in der Signed-Darstellung). Damit entspricht das Signed-Flag gewissermaßen dem Carry-Flag für Signed-Werte. Es wird hauptsächlich für „Signed“-Tests benötigt. Daher auch der Name.&lt;br /&gt;
&lt;br /&gt;
===Half Carry (H)===&lt;br /&gt;
Das Half-Carry-Flag hat die gleiche Aufgabe wie das Carry Flag, nur beschäftigt es sich mit einem Überlauf von Bit 3 nach Bit 4, also dem Übertrag zwischen dem oberen und dem unteren Nibble. Wie beim Carry-Flag gilt, dass das Flag nicht durch Inkrementieren bzw. Dekrementieren ausgelöst werden kann.&lt;br /&gt;
Das Haupteinsatzgebiet ist der Bereich der BCD-Arithmetik (binär codierte Dezimalzahlen), bei der jeweils 4 Bits eine Stelle einer Dezimalzahl repräsentieren.&lt;br /&gt;
&lt;br /&gt;
Kein Compiler benutzt das H-Flag. Findet man die Befehle &amp;lt;tt&amp;gt;brhc&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;brhs&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;clh&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;seh&amp;lt;/tt&amp;gt; in einem Disassemblerlisting, sind das entweder Daten oder es wurde in Assembler programmiert. Das H-Flag lässt sich bspw. dazu verwenden, zwei Zähler in einem Byte zu realisieren. So signalisiert das H-Flag in einem &amp;lt;u&amp;gt;Bit&amp;lt;/u&amp;gt;zähler, wann das nächste &amp;lt;u&amp;gt;Byte&amp;lt;/u&amp;gt; nachzuladen ist, indem man 2 addiert. Das reicht für 127 Bit.&lt;br /&gt;
&lt;br /&gt;
=== Transfer (T)===&lt;br /&gt;
Das T-Flag ist kein Statusbit im eigentlichen Sinne. Es steht dem Programmierer als 1-Bit-Speicher zur Verfügung. Der Zugriff erfolgt über die Befehle Bit Load (BLD), Bit Store (BST), Set-T (SET) und Clear-T (CLT) und wird sonst von keinen anderen Befehlen beeinflusst. Damit können Bits von einer Stelle schnell an eine andere kopiert oder getestet werden. Es gibt &amp;lt;i&amp;gt;keinen&amp;lt;/i&amp;gt; Befehl um das Bit zu kippen.&lt;br /&gt;
&lt;br /&gt;
Kein Compiler benutzt das T-Flag. Es kann jedoch in der Laufzeitbibliothek Verwendung finden, solche Kode-Abschnitte sind handoptimierter Assembler. &amp;lt;tt&amp;gt;avr-gcc&amp;lt;/tt&amp;gt; betrachtet das T-Flag als „flüchtig“ und darf in Unterprogrammen und Makros verändert werden.&lt;br /&gt;
&lt;br /&gt;
===Interrupt (I)===&lt;br /&gt;
Das Interrupt-Flag fällt hier etwas aus dem Rahmen; es hat nichts mit Berechnungen zu tun, sondern steuert, ob Interrupts im Controller zugelassen sind (siehe [[AVR-Tutorial: Interrupts]]).&lt;br /&gt;
&lt;br /&gt;
==Vergleiche==&lt;br /&gt;
Um einen Vergleich durchzuführen, wird intern eine Subtraktion der beiden Operanden vorgenommen. Das eigentliche Ergebnis der Subtraktion wird allerdings verworfen, es bleibt nur die neue Belegung der Flags übrig, die in weiterer Folge ausgewertet werden kann.&lt;br /&gt;
&lt;br /&gt;
===CP – Compare===&lt;br /&gt;
Vergleicht den Inhalt zweier Register miteinander. Prozessorintern wird dabei eine Subtraktion der beiden Register durchgeführt. Das eigentliche Subtraktionsergebnis wird allerdings verworfen, das Subtraktionsergebnis beeinflusst lediglich die Flags.&lt;br /&gt;
&lt;br /&gt;
===CPC – Compare with Carry===&lt;br /&gt;
Vergleicht den Inhalt zweier Register, wobei das Carry-Flag in den Vergleich mit einbezogen wird. Dieser Befehl wird für Arithmetik mit großen Variablen (16 oder 32 Bit) benötigt. Siehe [[AVR-Tutorial: Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===CPI – Compare Immediate===&lt;br /&gt;
Vergleicht den Inhalt eines Registers mit einer direkt angegebenen Konstanten. Der Befehl ist nur auf die Register r16…r31 anwendbar.&lt;br /&gt;
&lt;br /&gt;
==Bedingte Sprünge==&lt;br /&gt;
&lt;br /&gt;
Die bedingten Sprünge werten immer bestimmte Flags im Statusregister (SREG) aus. Es spielt dabei keine Rolle, ob dies nach einem Vergleichsbefehl oder einem sonstigen Befehl gemacht wird. Entscheidend ist einzig und allein der Zustand des abgefragten Flags. Die Namen der Sprungbefehle wurden allerdings so gewählt, daß sich im Befehlsnamen die Beziehung der Operanden direkt nach einem Compare-Befehl widerspiegelt. Zu beachten ist auch, daß die Flags nicht nur durch Vergleichsbefehle verändert werden, sondern auch durch arithmetische Operationen, Schiebebefehle und [[Bitmanipulation|logische Verknüpfungen]]. Da diese Information wichtig ist, ist auch in der bei Atmel/Microchip erhältlichen [https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf Übersicht über alle Assemblerbefehle] bei jedem Befehl angegeben, ob und wie er Flags beeinflusst. Ebenso ist dort eine kompakte Übersicht aller bedingten Sprünge zu finden. Beachten muss man jedoch, dass die bedingten Sprünge maximal 64 Worte weit springen können.&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenlose Zahlen ===&lt;br /&gt;
&lt;br /&gt;
;BRSH – Branch if Same or Higher: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) nicht gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist. BRSH ist identisch mit BRCC (Branch if Carry Cleared).&lt;br /&gt;
&lt;br /&gt;
;BRLO – Branch if Lower: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand kleiner dem zweiten Operanden ist. BRLO ist identisch mit BRCS (Branch if Carry Set).&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenbehaftete Zahlen ===&lt;br /&gt;
&lt;br /&gt;
;BRGE – Branch if Greater or Equal: Der Sprung wird durchgeführt, wenn das Signed-Flag (S) nicht gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist.&lt;br /&gt;
&lt;br /&gt;
;BRLT – Branch if Less Than: Der Sprung wird durchgeführt, wenn das Signed-Flag (S) gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der erste Operand kleiner als der zweite Operand ist.&lt;br /&gt;
&lt;br /&gt;
;BRMI – Branch if Minus: Der Sprung wird durchgeführt, wenn das Negativ-Flag (N) gesetzt ist, das Ergebnis der letzten Operation also negativ war.&lt;br /&gt;
&lt;br /&gt;
;BRPL – Branch if Plus: Der Sprung wird durchgeführt, wenn das Negativ-Flag (N) nicht gesetzt ist, das Ergebnis der letzten Operation also positiv war (einschließlich null).&lt;br /&gt;
&lt;br /&gt;
=== Sonstige bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
;BREQ – Branch if Equal: Der Sprung wird durchgeführt, wenn das Zero-Flag (Z) gesetzt ist. Ist nach einem Vergleich das Zero-Flag gesetzt, lieferte die interne Subtraktion also 0, so waren beide Operanden gleich.&lt;br /&gt;
&lt;br /&gt;
;BRNE – Branch if Not Equal: Der Sprung wird durchgeführt, wenn das Zero-Flag (Z) nicht gesetzt ist. Ist nach einem Vergleich das Zero-Flag nicht gesetzt, lieferte die interne Subtraktion also nicht 0, so waren beide Operanden verschieden.&lt;br /&gt;
&lt;br /&gt;
;BRCC – Branch if Carry Flag is Cleared: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) nicht gesetzt ist. Dieser Befehl wird oft für Arithmetik mit großen Variablen (16 oder 32&amp;amp;nbsp;Bit) bzw. im Zusammenhang mit Schiebeoperationen verwendet. BRCC ≡ BRSH&lt;br /&gt;
&lt;br /&gt;
;BRCS – Branch if Carry Flag is Set: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) gesetzt ist. Die Verwendung ist sehr ähnlich zu BRCC. BRCS ≡ BRLO&lt;br /&gt;
&lt;br /&gt;
=== Selten verwendete bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
;BRHC – Branch if Half Carry Flag is Cleared: Der Sprung wird durchgeführt, wenn das Half-Carry-Flag (H) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRHS – Branch if Half Carry Flag is Set: Der Sprung wird durchgeführt, wenn das Half-Carry-Flag (H) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRID – Branch if Global Interrupt is Disabled: Der Sprung wird durchgeführt, wenn das Interrupt-Flag (I) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRIE – Branch if Global Interrupt is Enabled: Der Sprung wird durchgeführt, wenn das Interrupt-Flag (I) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRTC – Branch if T Flag is Cleared: Der Sprung wird durchgeführt, wenn das T-Flag nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRTS – Branch if T Flag is Set: Der Sprung wird durchgeführt, wenn das T-Flag gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRVC – Branch if Overflow Cleared: Der Sprung wird durchgeführt, wenn das Overflow-Flag (V) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRVS – Branch if Overflow Set: Der Sprung wird durchgeführt, wenn das Overflow-Flag (V) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
==Beispiele==&lt;br /&gt;
&lt;br /&gt;
=== Entscheidungen ===&lt;br /&gt;
&lt;br /&gt;
In jedem Programm kommt früher oder später das Problem, die Ausführung von Codeteilen von irgendwelchen Zahlenwerten, die sich in anderen Registern befinden, abhängig zu machen. Sieht beispielsweise die Aufgabe vor, daß Register r18 auf 0 gesetzt werden soll, falls im Register r17 der Zahlenwert 25 enthalten ist und in allen anderen Fällen soll r18 auf 123 gesetzt werden, dann lautet der Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    cpi     r17, 25         ; vergleiche r17 mit der Konstante 25&lt;br /&gt;
    brne    nicht_gleich    ; wenn nicht gleich, dann mach bei nicht_gleich weiter&lt;br /&gt;
    ldi     r18, 0          ; hier stehen nun Anweisungen für den Fall,&lt;br /&gt;
                            ; dass R17 gleich 25 ist&lt;br /&gt;
    rjmp    weiter          ; meist will man den anderen Zweig nicht durchlaufen, darum der Sprung&lt;br /&gt;
nicht_gleich:&lt;br /&gt;
    ldi     r18, 123        ; hier stehen nun Anweisungen für den Fall,&lt;br /&gt;
                            ; dass R17 ungleich 25 ist&lt;br /&gt;
weiter:                     ; hier geht das Programm weiter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In ähnlicher Weise können die anderen bedingten Sprungbefehle eingesetzt werden, um die üblicherweise vorkommenden Vergleiche auf Gleichheit, Ungleichheit, „größer als“, „kleiner als“ zu realisieren.&lt;br /&gt;
&lt;br /&gt;
===Schleifenkonstrukte===&lt;br /&gt;
&lt;br /&gt;
Ein immer wiederkehrendes Muster in der Programmierung ist eine &#039;&#039;&#039;Schleife&#039;&#039;&#039;. Die einfachste Form einer Schleife ist die &#039;&#039;Zählschleife&#039;&#039;. Dabei wird ein Register von einem Startwert ausgehend eine gewisse Anzahl erhöht, bis ein Endwert erreicht wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    ldi     r17, 10         ; der Startwert sei in diesem Beispiel 10&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    inc     r17             ; erhöhe das Zählregister&lt;br /&gt;
    cpi     r17, 134        ; mit dem Endwert vergleichen&lt;br /&gt;
    brne    loop            ; und wenn der Endwert noch nicht erreicht ist,&lt;br /&gt;
                            ; wird bei der Marke loop ein weiterer Schleifendurchlauf ausgeführt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sehr oft ist es auch möglich, das Konstrukt umzukehren. Anstatt von einem Startwert aus zu inkrementieren genügt es, die Anzahl der gewünschten Schleifendurchläufe in ein Register zu laden und dieses Register zu dekrementieren. Dabei kann man von der Eigenschaft der Dekrementieranweisung Gebrauch machen, das Zero-Flag (Z) zu beeinflussen. Ist das Ergebnis des Dekrements gleich 0, so wird das Zero-Flag gesetzt, welches wiederum in der nachfolgenden BRNE-Anweisung für einen bedingten Sprung benutzt werden kann. Das vereinfacht die Schleife und spart eine Anweisung sowie einen Takt Ausführungszeit.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    ldi     r17, 124        ; Die Anzahl der Wiederholungen in ein Register laden&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    dec     r17             ; Schleifenzähler um 1 verringern, dabei wird das Zero-Flag beeinflusst&lt;br /&gt;
    brne    loop            ; wenn r17 noch nicht 0 geworden ist -&amp;gt; Schleife wiederholen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Literatur==&lt;br /&gt;
* [https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf AVR Instruction Set Manual]: Übersicht aller AVR-Befehle mit Angaben zur Beeinflussung der Statusbits&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Interrupts|&lt;br /&gt;
zurücklink=AVR-Tutorial: Interrupts|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Mehrfachverzweigungen|&lt;br /&gt;
vorlink=AVR-Tutorial: Mehrfachverzweigung}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Vergleiche]]&lt;br /&gt;
[[Category:AVR-Arithmetik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=107250</id>
		<title>AVR-Tutorial: Vergleiche</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=107250"/>
		<updated>2025-01-27T13:03:32Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* Carry (C) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Vergleiche und Entscheidungen sind in jeder Programmiersprache ein zentrales Mittel, um den Programmfluss abhängig von Bedingungen zu kontrollieren. In einem [[AVR]] spielen dazu vier Komponenten zusammen:&lt;br /&gt;
* Vergleichsbefehle,&lt;br /&gt;
* die Flags im Statusregister,&lt;br /&gt;
* bedingte Sprungbefehle,&lt;br /&gt;
* andere Befehle, die die Flags im Statusregister beeinflussen, wie z.&amp;amp;nbsp;B. die meisten arithmetischen Funktionen.&lt;br /&gt;
&lt;br /&gt;
Der Zusammenhang ist dabei folgender: Die Vergleichsbefehle führen einen Vergleich durch, zum Beispiel zwischen zwei Registern oder zwischen einem Register und einer Konstanten. Das Ergebnis des Vergleiches wird in den Flags abgelegt. Die bedingten Sprungbefehle werten die Flags aus und führen bei einem positiven Ergebnis den Sprung aus. Besonders der erste Satzteil ist wichtig! Den bedingten Sprungbefehlen ist es nämlich völlig egal, ob die Flags über Vergleichsbefehle oder über sonstige Befehle gesetzt wurden. Die Sprungbefehle werten einfach nur die Flags aus, wie auch immer diese zu ihrem Zustand kommen.&lt;br /&gt;
&lt;br /&gt;
==Flags==&lt;br /&gt;
&lt;br /&gt;
Die Flags sind Bits im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;. Ihre Aufgabe ist es, das Auftreten bestimmter Ereignisse, die während Berechnungen eintreten können, festzuhalten.&lt;br /&gt;
Speicherbefehle (LD, LDI, ST, MOV, …) haben auf dem AVR grundsätzlich keinen Einfluss auf das Statusregister. Will man den Inhalt eines Registers explizit testen (z.&amp;amp;nbsp;B. nach dem Laden aus dem SRAM), so kann man hierfür den TST-Befehl verwenden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | I | T | H | S | V | N | Z | C |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{{Byte|Bits im SREG| I|T|H|S|V|N|Z|C}}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Carry (C)===&lt;br /&gt;
Das Carry-Flag hält fest, ob es bei der letzten Berechnung einen Über- oder Unterlauf gab. Aber Achtung: Nicht alle arithmetischen Befehle verändern tatsächlich das Carry-Flag. So haben z.&amp;amp;nbsp;B. die Inkrementier- und Dekrementierbefehle keine Auswirkung auf dieses Flag.&lt;br /&gt;
&lt;br /&gt;
Weiterhin dient das C-Flag als „neuntes Bit“ für Schiebeoperationen und die Befehle &amp;lt;tt&amp;gt;adc&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sbc&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;sbic&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;cpc&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Zero (Z)===&lt;br /&gt;
Das Zero-Flag hält fest, ob das Ergebnis der letzten 8-Bit-Berechnung gleich 0 war oder nicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Achtung:&amp;lt;/b&amp;gt; Einige AVR-Befehle „kaskadieren“ das Z-Flag: Arithmetische Operationen mit C-Flag als Input, vor allem &amp;lt;tt&amp;gt;cpc&amp;lt;/tt&amp;gt; (compare with carry). Damit eignet es sich zur Nullfeststellung für beliebig breite Zahlen. IMHO keine weitere Prozessorarchitektur tut das.&lt;br /&gt;
&lt;br /&gt;
===Negative (N)===&lt;br /&gt;
Spiegelt den Zustand des höchstwertigen Bits (Bit 7) der letzten 8-Bit-Berechnung wider. In Zweierkomplement-Arithmetik — Standard bei allen Compilern für Ganzzahlen — bedeutet ein gesetztes Bit 7 eine negative Zahl. Das Bit kann also dazu genutzt werden um festzustellen, ob das Ergebnis einer Berechnung im Sinne einer Zweierkomplement-Arithmetik positiv oder negativ ist.&lt;br /&gt;
&lt;br /&gt;
===Overflow (V)===&lt;br /&gt;
Dieses Bit wird gesetzt, wenn bei einer Berechnung mit 2er-Komplement-Arithmetik ein Überlauf (Unterlauf) stattgefunden hat. Dies entspricht einem Überlauf von Bit 6 ins Bit 7.&lt;br /&gt;
&lt;br /&gt;
Der Übertrag, der bei der Addition/Subtraktion von Bit 6 auf Bit 7 auftritt, zeigt daher – wenn er vorhanden ist – an,&lt;br /&gt;
dass es sich hier um einen Überlauf (Overflow) des Zahlenbereichs handelt und das Ergebnis falsch ist.&lt;br /&gt;
Das ist allerdings nicht der Fall, wenn auch der Übertrag von Bit 7 nach Bit 8 (Carry) aufgetreten ist.&lt;br /&gt;
Daher ist das Overflow-Flag die [[AVR-Tutorial: Logik#XOR (Exklusives Oder)|XOR-Verknüpfung]] aus dem Übertrag von Bit 6 nach Bit 7 und dem Carry.&lt;br /&gt;
&lt;br /&gt;
Beispiele für die Anwendung des V-Flags finden sich in [[AVR Arithmetik/Saturierung|saturierter Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===Signed (S)===&lt;br /&gt;
Das Signed-Bit ergibt sich aus der Antivalenz (exklusives Oder) der Flags N und V, also S = N XOR V.&lt;br /&gt;
Mit Hilfe des Signed-Flags können vorzeichenbehaftete Werte miteinander verglichen werden. Ist nach einem Vergleich zweier Register S=1, so ist der Wert des ersten Registers kleiner dem zweiten (in der Signed-Darstellung). Damit entspricht das Signed-Flag gewissermaßen dem Carry-Flag für Signed-Werte. Es wird hauptsächlich für „Signed“-Tests benötigt. Daher auch der Name.&lt;br /&gt;
&lt;br /&gt;
===Half Carry (H)===&lt;br /&gt;
Das Half-Carry-Flag hat die gleiche Aufgabe wie das Carry Flag, nur beschäftigt es sich mit einem Überlauf von Bit 3 nach Bit 4, also dem Übertrag zwischen dem oberen und dem unteren Nibble. Wie beim Carry-Flag gilt, dass das Flag nicht durch Inkrementieren bzw. Dekrementieren ausgelöst werden kann.&lt;br /&gt;
Das Haupteinsatzgebiet ist der Bereich der BCD-Arithmetik (binär codierte Dezimalzahlen), bei der jeweils 4 Bits eine Stelle einer Dezimalzahl repräsentieren.&lt;br /&gt;
&lt;br /&gt;
Kein Compiler benutzt das H-Flag. Findet man die Befehle &amp;lt;tt&amp;gt;brhc&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;brhs&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;clh&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;seh&amp;lt;/tt&amp;gt; in einem Disassemblerlisting, sind das entweder Daten oder es wurde in Assembler programmiert. Das H-Flag lässt sich bspw. dazu verwenden, zwei Zähler in einem Byte zu realisieren. So signalisiert das H-Flag in einem &amp;lt;u&amp;gt;Bit&amp;lt;/u&amp;gt;zähler, wann das nächste &amp;lt;u&amp;gt;Byte&amp;lt;/u&amp;gt; nachzuladen ist, indem man 2 addiert. Das reicht für 127 Bit.&lt;br /&gt;
&lt;br /&gt;
=== Transfer (T)===&lt;br /&gt;
Das T-Flag ist kein Statusbit im eigentlichen Sinne. Es steht dem Programmierer als 1-Bit-Speicher zur Verfügung. Der Zugriff erfolgt über die Befehle Bit Load (BLD), Bit Store (BST), Set-T (SET) und Clear-T (CLT) und wird sonst von keinen anderen Befehlen beeinflusst. Damit können Bits von einer Stelle schnell an eine andere kopiert oder getestet werden. Es gibt &amp;lt;i&amp;gt;keinen&amp;lt;/i&amp;gt; Befehl um das Bit zu kippen.&lt;br /&gt;
&lt;br /&gt;
Kein Compiler benutzt das T-Flag. Es kann jedoch in der Laufzeitbibliothek Verwendung finden, solche Kode-Abschnitte sind handoptimierter Assembler. &amp;lt;tt&amp;gt;avr-gcc&amp;lt;/tt&amp;gt; betrachtet das T-Flag als „flüchtig“ und darf in Unterprogrammen und Makros verändert werden.&lt;br /&gt;
&lt;br /&gt;
===Interrupt (I)===&lt;br /&gt;
Das Interrupt-Flag fällt hier etwas aus dem Rahmen; es hat nichts mit Berechnungen zu tun, sondern steuert, ob Interrupts im Controller zugelassen sind (siehe [[AVR-Tutorial: Interrupts]]).&lt;br /&gt;
&lt;br /&gt;
==Vergleiche==&lt;br /&gt;
Um einen Vergleich durchzuführen, wird intern eine Subtraktion der beiden Operanden vorgenommen. Das eigentliche Ergebnis der Subtraktion wird allerdings verworfen, es bleibt nur die neue Belegung der Flags übrig, die in weiterer Folge ausgewertet werden kann.&lt;br /&gt;
&lt;br /&gt;
===CP – Compare===&lt;br /&gt;
Vergleicht den Inhalt zweier Register miteinander. Prozessorintern wird dabei eine Subtraktion der beiden Register durchgeführt. Das eigentliche Subtraktionsergebnis wird allerdings verworfen, das Subtraktionsergebnis beeinflusst lediglich die Flags.&lt;br /&gt;
&lt;br /&gt;
===CPC – Compare with Carry===&lt;br /&gt;
Vergleicht den Inhalt zweier Register, wobei das Carry-Flag in den Vergleich mit einbezogen wird. Dieser Befehl wird für Arithmetik mit großen Variablen (16 oder 32 Bit) benötigt. Siehe [[AVR-Tutorial: Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===CPI – Compare Immediate===&lt;br /&gt;
Vergleicht den Inhalt eines Registers mit einer direkt angegebenen Konstanten. Der Befehl ist nur auf die Register r16…r31 anwendbar.&lt;br /&gt;
&lt;br /&gt;
==Bedingte Sprünge==&lt;br /&gt;
&lt;br /&gt;
Die bedingten Sprünge werten immer bestimmte Flags im Statusregister (SREG) aus. Es spielt dabei keine Rolle, ob dies nach einem Vergleichsbefehl oder einem sonstigen Befehl gemacht wird. Entscheidend ist einzig und allein der Zustand des abgefragten Flags. Die Namen der Sprungbefehle wurden allerdings so gewählt, daß sich im Befehlsnamen die Beziehung der Operanden direkt nach einem Compare-Befehl widerspiegelt. Zu beachten ist auch, daß die Flags nicht nur durch Vergleichsbefehle verändert werden, sondern auch durch arithmetische Operationen, Schiebebefehle und [[Bitmanipulation|logische Verknüpfungen]]. Da diese Information wichtig ist, ist auch in der bei Atmel/Microchip erhältlichen [https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf Übersicht über alle Assemblerbefehle] bei jedem Befehl angegeben, ob und wie er Flags beeinflusst. Ebenso ist dort eine kompakte Übersicht aller bedingten Sprünge zu finden. Beachten muss man jedoch, dass die bedingten Sprünge maximal 64 Worte weit springen können.&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenlose Zahlen ===&lt;br /&gt;
&lt;br /&gt;
;BRSH – Branch if Same or Higher: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) nicht gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist. BRSH ist identisch mit BRCC (Branch if Carry Cleared).&lt;br /&gt;
&lt;br /&gt;
;BRLO – Branch if Lower: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand kleiner dem zweiten Operanden ist. BRLO ist identisch mit BRCS (Branch if Carry Set).&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenbehaftete Zahlen ===&lt;br /&gt;
&lt;br /&gt;
;BRGE – Branch if Greater or Equal: Der Sprung wird durchgeführt, wenn das Signed-Flag (S) nicht gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist.&lt;br /&gt;
&lt;br /&gt;
;BRLT – Branch if Less Than: Der Sprung wird durchgeführt, wenn das Signed-Flag (S) gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der erste Operand kleiner als der zweite Operand ist.&lt;br /&gt;
&lt;br /&gt;
;BRMI – Branch if Minus: Der Sprung wird durchgeführt, wenn das Negativ-Flag (N) gesetzt ist, das Ergebnis der letzten Operation also negativ war.&lt;br /&gt;
&lt;br /&gt;
;BRPL – Branch if Plus: Der Sprung wird durchgeführt, wenn das Negativ-Flag (N) nicht gesetzt ist, das Ergebnis der letzten Operation also positiv war (einschließlich null).&lt;br /&gt;
&lt;br /&gt;
=== Sonstige bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
;BREQ – Branch if Equal: Der Sprung wird durchgeführt, wenn das Zero-Flag (Z) gesetzt ist. Ist nach einem Vergleich das Zero-Flag gesetzt, lieferte die interne Subtraktion also 0, so waren beide Operanden gleich.&lt;br /&gt;
&lt;br /&gt;
;BRNE – Branch if Not Equal: Der Sprung wird durchgeführt, wenn das Zero-Flag (Z) nicht gesetzt ist. Ist nach einem Vergleich das Zero-Flag nicht gesetzt, lieferte die interne Subtraktion also nicht 0, so waren beide Operanden verschieden.&lt;br /&gt;
&lt;br /&gt;
;BRCC – Branch if Carry Flag is Cleared: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) nicht gesetzt ist. Dieser Befehl wird oft für Arithmetik mit großen Variablen (16 oder 32&amp;amp;nbsp;Bit) bzw. im Zusammenhang mit Schiebeoperationen verwendet. BRCC ≡ BRSH&lt;br /&gt;
&lt;br /&gt;
;BRCS – Branch if Carry Flag is Set: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) gesetzt ist. Die Verwendung ist sehr ähnlich zu BRCC. BRCS ≡ BRLO&lt;br /&gt;
&lt;br /&gt;
=== Selten verwendete bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
;BRHC – Branch if Half Carry Flag is Cleared: Der Sprung wird durchgeführt, wenn das Half-Carry-Flag (H) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRHS – Branch if Half Carry Flag is Set: Der Sprung wird durchgeführt, wenn das Half-Carry-Flag (H) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRID – Branch if Global Interrupt is Disabled: Der Sprung wird durchgeführt, wenn das Interrupt-Flag (I) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRIE – Branch if Global Interrupt is Enabled: Der Sprung wird durchgeführt, wenn das Interrupt-Flag (I) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRTC – Branch if T Flag is Cleared: Der Sprung wird durchgeführt, wenn das T-Flag nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRTS – Branch if T Flag is Set: Der Sprung wird durchgeführt, wenn das T-Flag gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRVC – Branch if Overflow Cleared: Der Sprung wird durchgeführt, wenn das Overflow-Flag (V) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRVS – Branch if Overflow Set: Der Sprung wird durchgeführt, wenn das Overflow-Flag (V) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
==Beispiele==&lt;br /&gt;
&lt;br /&gt;
=== Entscheidungen ===&lt;br /&gt;
&lt;br /&gt;
In jedem Programm kommt früher oder später das Problem, die Ausführung von Codeteilen von irgendwelchen Zahlenwerten, die sich in anderen Registern befinden, abhängig zu machen. Sieht beispielsweise die Aufgabe vor, daß Register r18 auf 0 gesetzt werden soll, falls im Register r17 der Zahlenwert 25 enthalten ist und in allen anderen Fällen soll r18 auf 123 gesetzt werden, dann lautet der Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    cpi     r17, 25         ; vergleiche r17 mit der Konstante 25&lt;br /&gt;
    brne    nicht_gleich    ; wenn nicht gleich, dann mach bei nicht_gleich weiter&lt;br /&gt;
    ldi     r18, 0          ; hier stehen nun Anweisungen für den Fall,&lt;br /&gt;
                            ; dass R17 gleich 25 ist&lt;br /&gt;
    rjmp    weiter          ; meist will man den anderen Zweig nicht durchlaufen, darum der Sprung&lt;br /&gt;
nicht_gleich:&lt;br /&gt;
    ldi     r18, 123        ; hier stehen nun Anweisungen für den Fall,&lt;br /&gt;
                            ; dass R17 ungleich 25 ist&lt;br /&gt;
weiter:                     ; hier geht das Programm weiter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In ähnlicher Weise können die anderen bedingten Sprungbefehle eingesetzt werden, um die üblicherweise vorkommenden Vergleiche auf Gleichheit, Ungleichheit, „größer als“, „kleiner als“ zu realisieren.&lt;br /&gt;
&lt;br /&gt;
===Schleifenkonstrukte===&lt;br /&gt;
&lt;br /&gt;
Ein immer wiederkehrendes Muster in der Programmierung ist eine &#039;&#039;&#039;Schleife&#039;&#039;&#039;. Die einfachste Form einer Schleife ist die &#039;&#039;Zählschleife&#039;&#039;. Dabei wird ein Register von einem Startwert ausgehend eine gewisse Anzahl erhöht, bis ein Endwert erreicht wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    ldi     r17, 10         ; der Startwert sei in diesem Beispiel 10&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    inc     r17             ; erhöhe das Zählregister&lt;br /&gt;
    cpi     r17, 134        ; mit dem Endwert vergleichen&lt;br /&gt;
    brne    loop            ; und wenn der Endwert noch nicht erreicht ist,&lt;br /&gt;
                            ; wird bei der Marke loop ein weiterer Schleifendurchlauf ausgeführt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sehr oft ist es auch möglich, das Konstrukt umzukehren. Anstatt von einem Startwert aus zu inkrementieren genügt es, die Anzahl der gewünschten Schleifendurchläufe in ein Register zu laden und dieses Register zu dekrementieren. Dabei kann man von der Eigenschaft der Dekrementieranweisung Gebrauch machen, das Zero-Flag (Z) zu beeinflussen. Ist das Ergebnis des Dekrements gleich 0, so wird das Zero-Flag gesetzt, welches wiederum in der nachfolgenden BRNE-Anweisung für einen bedingten Sprung benutzt werden kann. Das vereinfacht die Schleife und spart eine Anweisung sowie einen Takt Ausführungszeit.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    ldi     r17, 124        ; Die Anzahl der Wiederholungen in ein Register laden&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    dec     r17             ; Schleifenzähler um 1 verringern, dabei wird das Zero-Flag beeinflusst&lt;br /&gt;
    brne    loop            ; wenn r17 noch nicht 0 geworden ist -&amp;gt; Schleife wiederholen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Literatur==&lt;br /&gt;
* [https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf AVR Instruction Set Manual]: Übersicht aller AVR-Befehle mit Angaben zur Beeinflussung der Statusbits&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Interrupts|&lt;br /&gt;
zurücklink=AVR-Tutorial: Interrupts|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Mehrfachverzweigungen|&lt;br /&gt;
vorlink=AVR-Tutorial: Mehrfachverzweigung}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Vergleiche]]&lt;br /&gt;
[[Category:AVR-Arithmetik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=107249</id>
		<title>AVR-Tutorial: Vergleiche</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=107249"/>
		<updated>2025-01-27T13:00:23Z</updated>

		<summary type="html">&lt;p&gt;Heha: Kaskadiertes Z-Flag&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Vergleiche und Entscheidungen sind in jeder Programmiersprache ein zentrales Mittel, um den Programmfluss abhängig von Bedingungen zu kontrollieren. In einem [[AVR]] spielen dazu vier Komponenten zusammen:&lt;br /&gt;
* Vergleichsbefehle,&lt;br /&gt;
* die Flags im Statusregister,&lt;br /&gt;
* bedingte Sprungbefehle,&lt;br /&gt;
* andere Befehle, die die Flags im Statusregister beeinflussen, wie z.&amp;amp;nbsp;B. die meisten arithmetischen Funktionen.&lt;br /&gt;
&lt;br /&gt;
Der Zusammenhang ist dabei folgender: Die Vergleichsbefehle führen einen Vergleich durch, zum Beispiel zwischen zwei Registern oder zwischen einem Register und einer Konstanten. Das Ergebnis des Vergleiches wird in den Flags abgelegt. Die bedingten Sprungbefehle werten die Flags aus und führen bei einem positiven Ergebnis den Sprung aus. Besonders der erste Satzteil ist wichtig! Den bedingten Sprungbefehlen ist es nämlich völlig egal, ob die Flags über Vergleichsbefehle oder über sonstige Befehle gesetzt wurden. Die Sprungbefehle werten einfach nur die Flags aus, wie auch immer diese zu ihrem Zustand kommen.&lt;br /&gt;
&lt;br /&gt;
==Flags==&lt;br /&gt;
&lt;br /&gt;
Die Flags sind Bits im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;. Ihre Aufgabe ist es, das Auftreten bestimmter Ereignisse, die während Berechnungen eintreten können, festzuhalten.&lt;br /&gt;
Speicherbefehle (LD, LDI, ST, MOV, …) haben auf dem AVR grundsätzlich keinen Einfluss auf das Statusregister. Will man den Inhalt eines Registers explizit testen (z.&amp;amp;nbsp;B. nach dem Laden aus dem SRAM), so kann man hierfür den TST-Befehl verwenden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | I | T | H | S | V | N | Z | C |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{{Byte|Bits im SREG| I|T|H|S|V|N|Z|C}}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Carry (C)===&lt;br /&gt;
Das Carry-Flag hält fest, ob es bei der letzten Berechnung einen Über- oder Unterlauf gab. Aber Achtung: Nicht alle arithmetischen Befehle verändern tatsächlich das Carry-Flag. So haben z.&amp;amp;nbsp;B. die Inkrementier- und Dekrementierbefehle keine Auswirkung auf dieses Flag.&lt;br /&gt;
&lt;br /&gt;
===Zero (Z)===&lt;br /&gt;
Das Zero-Flag hält fest, ob das Ergebnis der letzten 8-Bit-Berechnung gleich 0 war oder nicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Achtung:&amp;lt;/b&amp;gt; Einige AVR-Befehle „kaskadieren“ das Z-Flag: Arithmetische Operationen mit C-Flag als Input, vor allem &amp;lt;tt&amp;gt;cpc&amp;lt;/tt&amp;gt; (compare with carry). Damit eignet es sich zur Nullfeststellung für beliebig breite Zahlen. IMHO keine weitere Prozessorarchitektur tut das.&lt;br /&gt;
&lt;br /&gt;
===Negative (N)===&lt;br /&gt;
Spiegelt den Zustand des höchstwertigen Bits (Bit 7) der letzten 8-Bit-Berechnung wider. In Zweierkomplement-Arithmetik — Standard bei allen Compilern für Ganzzahlen — bedeutet ein gesetztes Bit 7 eine negative Zahl. Das Bit kann also dazu genutzt werden um festzustellen, ob das Ergebnis einer Berechnung im Sinne einer Zweierkomplement-Arithmetik positiv oder negativ ist.&lt;br /&gt;
&lt;br /&gt;
===Overflow (V)===&lt;br /&gt;
Dieses Bit wird gesetzt, wenn bei einer Berechnung mit 2er-Komplement-Arithmetik ein Überlauf (Unterlauf) stattgefunden hat. Dies entspricht einem Überlauf von Bit 6 ins Bit 7.&lt;br /&gt;
&lt;br /&gt;
Der Übertrag, der bei der Addition/Subtraktion von Bit 6 auf Bit 7 auftritt, zeigt daher – wenn er vorhanden ist – an,&lt;br /&gt;
dass es sich hier um einen Überlauf (Overflow) des Zahlenbereichs handelt und das Ergebnis falsch ist.&lt;br /&gt;
Das ist allerdings nicht der Fall, wenn auch der Übertrag von Bit 7 nach Bit 8 (Carry) aufgetreten ist.&lt;br /&gt;
Daher ist das Overflow-Flag die [[AVR-Tutorial: Logik#XOR (Exklusives Oder)|XOR-Verknüpfung]] aus dem Übertrag von Bit 6 nach Bit 7 und dem Carry.&lt;br /&gt;
&lt;br /&gt;
Beispiele für die Anwendung des V-Flags finden sich in [[AVR Arithmetik/Saturierung|saturierter Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===Signed (S)===&lt;br /&gt;
Das Signed-Bit ergibt sich aus der Antivalenz (exklusives Oder) der Flags N und V, also S = N XOR V.&lt;br /&gt;
Mit Hilfe des Signed-Flags können vorzeichenbehaftete Werte miteinander verglichen werden. Ist nach einem Vergleich zweier Register S=1, so ist der Wert des ersten Registers kleiner dem zweiten (in der Signed-Darstellung). Damit entspricht das Signed-Flag gewissermaßen dem Carry-Flag für Signed-Werte. Es wird hauptsächlich für „Signed“-Tests benötigt. Daher auch der Name.&lt;br /&gt;
&lt;br /&gt;
===Half Carry (H)===&lt;br /&gt;
Das Half-Carry-Flag hat die gleiche Aufgabe wie das Carry Flag, nur beschäftigt es sich mit einem Überlauf von Bit 3 nach Bit 4, also dem Übertrag zwischen dem oberen und dem unteren Nibble. Wie beim Carry-Flag gilt, dass das Flag nicht durch Inkrementieren bzw. Dekrementieren ausgelöst werden kann.&lt;br /&gt;
Das Haupteinsatzgebiet ist der Bereich der BCD-Arithmetik (binär codierte Dezimalzahlen), bei der jeweils 4 Bits eine Stelle einer Dezimalzahl repräsentieren.&lt;br /&gt;
&lt;br /&gt;
Kein Compiler benutzt das H-Flag. Findet man die Befehle &amp;lt;tt&amp;gt;brhc&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;brhs&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;clh&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;seh&amp;lt;/tt&amp;gt; in einem Disassemblerlisting, sind das entweder Daten oder es wurde in Assembler programmiert. Das H-Flag lässt sich bspw. dazu verwenden, zwei Zähler in einem Byte zu realisieren. So signalisiert das H-Flag in einem &amp;lt;u&amp;gt;Bit&amp;lt;/u&amp;gt;zähler, wann das nächste &amp;lt;u&amp;gt;Byte&amp;lt;/u&amp;gt; nachzuladen ist, indem man 2 addiert. Das reicht für 127 Bit.&lt;br /&gt;
&lt;br /&gt;
=== Transfer (T)===&lt;br /&gt;
Das T-Flag ist kein Statusbit im eigentlichen Sinne. Es steht dem Programmierer als 1-Bit-Speicher zur Verfügung. Der Zugriff erfolgt über die Befehle Bit Load (BLD), Bit Store (BST), Set-T (SET) und Clear-T (CLT) und wird sonst von keinen anderen Befehlen beeinflusst. Damit können Bits von einer Stelle schnell an eine andere kopiert oder getestet werden. Es gibt &amp;lt;i&amp;gt;keinen&amp;lt;/i&amp;gt; Befehl um das Bit zu kippen.&lt;br /&gt;
&lt;br /&gt;
Kein Compiler benutzt das T-Flag. Es kann jedoch in der Laufzeitbibliothek Verwendung finden, solche Kode-Abschnitte sind handoptimierter Assembler. &amp;lt;tt&amp;gt;avr-gcc&amp;lt;/tt&amp;gt; betrachtet das T-Flag als „flüchtig“ und darf in Unterprogrammen und Makros verändert werden.&lt;br /&gt;
&lt;br /&gt;
===Interrupt (I)===&lt;br /&gt;
Das Interrupt-Flag fällt hier etwas aus dem Rahmen; es hat nichts mit Berechnungen zu tun, sondern steuert, ob Interrupts im Controller zugelassen sind (siehe [[AVR-Tutorial: Interrupts]]).&lt;br /&gt;
&lt;br /&gt;
==Vergleiche==&lt;br /&gt;
Um einen Vergleich durchzuführen, wird intern eine Subtraktion der beiden Operanden vorgenommen. Das eigentliche Ergebnis der Subtraktion wird allerdings verworfen, es bleibt nur die neue Belegung der Flags übrig, die in weiterer Folge ausgewertet werden kann.&lt;br /&gt;
&lt;br /&gt;
===CP – Compare===&lt;br /&gt;
Vergleicht den Inhalt zweier Register miteinander. Prozessorintern wird dabei eine Subtraktion der beiden Register durchgeführt. Das eigentliche Subtraktionsergebnis wird allerdings verworfen, das Subtraktionsergebnis beeinflusst lediglich die Flags.&lt;br /&gt;
&lt;br /&gt;
===CPC – Compare with Carry===&lt;br /&gt;
Vergleicht den Inhalt zweier Register, wobei das Carry-Flag in den Vergleich mit einbezogen wird. Dieser Befehl wird für Arithmetik mit großen Variablen (16 oder 32 Bit) benötigt. Siehe [[AVR-Tutorial: Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===CPI – Compare Immediate===&lt;br /&gt;
Vergleicht den Inhalt eines Registers mit einer direkt angegebenen Konstanten. Der Befehl ist nur auf die Register r16…r31 anwendbar.&lt;br /&gt;
&lt;br /&gt;
==Bedingte Sprünge==&lt;br /&gt;
&lt;br /&gt;
Die bedingten Sprünge werten immer bestimmte Flags im Statusregister (SREG) aus. Es spielt dabei keine Rolle, ob dies nach einem Vergleichsbefehl oder einem sonstigen Befehl gemacht wird. Entscheidend ist einzig und allein der Zustand des abgefragten Flags. Die Namen der Sprungbefehle wurden allerdings so gewählt, daß sich im Befehlsnamen die Beziehung der Operanden direkt nach einem Compare-Befehl widerspiegelt. Zu beachten ist auch, daß die Flags nicht nur durch Vergleichsbefehle verändert werden, sondern auch durch arithmetische Operationen, Schiebebefehle und [[Bitmanipulation|logische Verknüpfungen]]. Da diese Information wichtig ist, ist auch in der bei Atmel/Microchip erhältlichen [https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf Übersicht über alle Assemblerbefehle] bei jedem Befehl angegeben, ob und wie er Flags beeinflusst. Ebenso ist dort eine kompakte Übersicht aller bedingten Sprünge zu finden. Beachten muss man jedoch, dass die bedingten Sprünge maximal 64 Worte weit springen können.&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenlose Zahlen ===&lt;br /&gt;
&lt;br /&gt;
;BRSH – Branch if Same or Higher: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) nicht gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist. BRSH ist identisch mit BRCC (Branch if Carry Cleared).&lt;br /&gt;
&lt;br /&gt;
;BRLO – Branch if Lower: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand kleiner dem zweiten Operanden ist. BRLO ist identisch mit BRCS (Branch if Carry Set).&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenbehaftete Zahlen ===&lt;br /&gt;
&lt;br /&gt;
;BRGE – Branch if Greater or Equal: Der Sprung wird durchgeführt, wenn das Signed-Flag (S) nicht gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist.&lt;br /&gt;
&lt;br /&gt;
;BRLT – Branch if Less Than: Der Sprung wird durchgeführt, wenn das Signed-Flag (S) gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der erste Operand kleiner als der zweite Operand ist.&lt;br /&gt;
&lt;br /&gt;
;BRMI – Branch if Minus: Der Sprung wird durchgeführt, wenn das Negativ-Flag (N) gesetzt ist, das Ergebnis der letzten Operation also negativ war.&lt;br /&gt;
&lt;br /&gt;
;BRPL – Branch if Plus: Der Sprung wird durchgeführt, wenn das Negativ-Flag (N) nicht gesetzt ist, das Ergebnis der letzten Operation also positiv war (einschließlich null).&lt;br /&gt;
&lt;br /&gt;
=== Sonstige bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
;BREQ – Branch if Equal: Der Sprung wird durchgeführt, wenn das Zero-Flag (Z) gesetzt ist. Ist nach einem Vergleich das Zero-Flag gesetzt, lieferte die interne Subtraktion also 0, so waren beide Operanden gleich.&lt;br /&gt;
&lt;br /&gt;
;BRNE – Branch if Not Equal: Der Sprung wird durchgeführt, wenn das Zero-Flag (Z) nicht gesetzt ist. Ist nach einem Vergleich das Zero-Flag nicht gesetzt, lieferte die interne Subtraktion also nicht 0, so waren beide Operanden verschieden.&lt;br /&gt;
&lt;br /&gt;
;BRCC – Branch if Carry Flag is Cleared: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) nicht gesetzt ist. Dieser Befehl wird oft für Arithmetik mit großen Variablen (16 oder 32&amp;amp;nbsp;Bit) bzw. im Zusammenhang mit Schiebeoperationen verwendet. BRCC ≡ BRSH&lt;br /&gt;
&lt;br /&gt;
;BRCS – Branch if Carry Flag is Set: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) gesetzt ist. Die Verwendung ist sehr ähnlich zu BRCC. BRCS ≡ BRLO&lt;br /&gt;
&lt;br /&gt;
=== Selten verwendete bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
;BRHC – Branch if Half Carry Flag is Cleared: Der Sprung wird durchgeführt, wenn das Half-Carry-Flag (H) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRHS – Branch if Half Carry Flag is Set: Der Sprung wird durchgeführt, wenn das Half-Carry-Flag (H) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRID – Branch if Global Interrupt is Disabled: Der Sprung wird durchgeführt, wenn das Interrupt-Flag (I) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRIE – Branch if Global Interrupt is Enabled: Der Sprung wird durchgeführt, wenn das Interrupt-Flag (I) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRTC – Branch if T Flag is Cleared: Der Sprung wird durchgeführt, wenn das T-Flag nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRTS – Branch if T Flag is Set: Der Sprung wird durchgeführt, wenn das T-Flag gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRVC – Branch if Overflow Cleared: Der Sprung wird durchgeführt, wenn das Overflow-Flag (V) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRVS – Branch if Overflow Set: Der Sprung wird durchgeführt, wenn das Overflow-Flag (V) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
==Beispiele==&lt;br /&gt;
&lt;br /&gt;
=== Entscheidungen ===&lt;br /&gt;
&lt;br /&gt;
In jedem Programm kommt früher oder später das Problem, die Ausführung von Codeteilen von irgendwelchen Zahlenwerten, die sich in anderen Registern befinden, abhängig zu machen. Sieht beispielsweise die Aufgabe vor, daß Register r18 auf 0 gesetzt werden soll, falls im Register r17 der Zahlenwert 25 enthalten ist und in allen anderen Fällen soll r18 auf 123 gesetzt werden, dann lautet der Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    cpi     r17, 25         ; vergleiche r17 mit der Konstante 25&lt;br /&gt;
    brne    nicht_gleich    ; wenn nicht gleich, dann mach bei nicht_gleich weiter&lt;br /&gt;
    ldi     r18, 0          ; hier stehen nun Anweisungen für den Fall,&lt;br /&gt;
                            ; dass R17 gleich 25 ist&lt;br /&gt;
    rjmp    weiter          ; meist will man den anderen Zweig nicht durchlaufen, darum der Sprung&lt;br /&gt;
nicht_gleich:&lt;br /&gt;
    ldi     r18, 123        ; hier stehen nun Anweisungen für den Fall,&lt;br /&gt;
                            ; dass R17 ungleich 25 ist&lt;br /&gt;
weiter:                     ; hier geht das Programm weiter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In ähnlicher Weise können die anderen bedingten Sprungbefehle eingesetzt werden, um die üblicherweise vorkommenden Vergleiche auf Gleichheit, Ungleichheit, „größer als“, „kleiner als“ zu realisieren.&lt;br /&gt;
&lt;br /&gt;
===Schleifenkonstrukte===&lt;br /&gt;
&lt;br /&gt;
Ein immer wiederkehrendes Muster in der Programmierung ist eine &#039;&#039;&#039;Schleife&#039;&#039;&#039;. Die einfachste Form einer Schleife ist die &#039;&#039;Zählschleife&#039;&#039;. Dabei wird ein Register von einem Startwert ausgehend eine gewisse Anzahl erhöht, bis ein Endwert erreicht wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    ldi     r17, 10         ; der Startwert sei in diesem Beispiel 10&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    inc     r17             ; erhöhe das Zählregister&lt;br /&gt;
    cpi     r17, 134        ; mit dem Endwert vergleichen&lt;br /&gt;
    brne    loop            ; und wenn der Endwert noch nicht erreicht ist,&lt;br /&gt;
                            ; wird bei der Marke loop ein weiterer Schleifendurchlauf ausgeführt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sehr oft ist es auch möglich, das Konstrukt umzukehren. Anstatt von einem Startwert aus zu inkrementieren genügt es, die Anzahl der gewünschten Schleifendurchläufe in ein Register zu laden und dieses Register zu dekrementieren. Dabei kann man von der Eigenschaft der Dekrementieranweisung Gebrauch machen, das Zero-Flag (Z) zu beeinflussen. Ist das Ergebnis des Dekrements gleich 0, so wird das Zero-Flag gesetzt, welches wiederum in der nachfolgenden BRNE-Anweisung für einen bedingten Sprung benutzt werden kann. Das vereinfacht die Schleife und spart eine Anweisung sowie einen Takt Ausführungszeit.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    ldi     r17, 124        ; Die Anzahl der Wiederholungen in ein Register laden&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    dec     r17             ; Schleifenzähler um 1 verringern, dabei wird das Zero-Flag beeinflusst&lt;br /&gt;
    brne    loop            ; wenn r17 noch nicht 0 geworden ist -&amp;gt; Schleife wiederholen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Literatur==&lt;br /&gt;
* [https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf AVR Instruction Set Manual]: Übersicht aller AVR-Befehle mit Angaben zur Beeinflussung der Statusbits&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Interrupts|&lt;br /&gt;
zurücklink=AVR-Tutorial: Interrupts|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Mehrfachverzweigungen|&lt;br /&gt;
vorlink=AVR-Tutorial: Mehrfachverzweigung}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Vergleiche]]&lt;br /&gt;
[[Category:AVR-Arithmetik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=107248</id>
		<title>AVR-Tutorial: Vergleiche</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=107248"/>
		<updated>2025-01-27T12:54:06Z</updated>

		<summary type="html">&lt;p&gt;Heha: Negative (N)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Vergleiche und Entscheidungen sind in jeder Programmiersprache ein zentrales Mittel, um den Programmfluss abhängig von Bedingungen zu kontrollieren. In einem [[AVR]] spielen dazu vier Komponenten zusammen:&lt;br /&gt;
* Vergleichsbefehle,&lt;br /&gt;
* die Flags im Statusregister,&lt;br /&gt;
* bedingte Sprungbefehle,&lt;br /&gt;
* andere Befehle, die die Flags im Statusregister beeinflussen, wie z.&amp;amp;nbsp;B. die meisten arithmetischen Funktionen.&lt;br /&gt;
&lt;br /&gt;
Der Zusammenhang ist dabei folgender: Die Vergleichsbefehle führen einen Vergleich durch, zum Beispiel zwischen zwei Registern oder zwischen einem Register und einer Konstanten. Das Ergebnis des Vergleiches wird in den Flags abgelegt. Die bedingten Sprungbefehle werten die Flags aus und führen bei einem positiven Ergebnis den Sprung aus. Besonders der erste Satzteil ist wichtig! Den bedingten Sprungbefehlen ist es nämlich völlig egal, ob die Flags über Vergleichsbefehle oder über sonstige Befehle gesetzt wurden. Die Sprungbefehle werten einfach nur die Flags aus, wie auch immer diese zu ihrem Zustand kommen.&lt;br /&gt;
&lt;br /&gt;
==Flags==&lt;br /&gt;
&lt;br /&gt;
Die Flags sind Bits im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;. Ihre Aufgabe ist es, das Auftreten bestimmter Ereignisse, die während Berechnungen eintreten können, festzuhalten.&lt;br /&gt;
Speicherbefehle (LD, LDI, ST, MOV, …) haben auf dem AVR grundsätzlich keinen Einfluss auf das Statusregister. Will man den Inhalt eines Registers explizit testen (z.&amp;amp;nbsp;B. nach dem Laden aus dem SRAM), so kann man hierfür den TST-Befehl verwenden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | I | T | H | S | V | N | Z | C |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{{Byte|Bits im SREG| I|T|H|S|V|N|Z|C}}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Carry (C)===&lt;br /&gt;
Das Carry-Flag hält fest, ob es bei der letzten Berechnung einen Über- oder Unterlauf gab. Aber Achtung: Nicht alle arithmetischen Befehle verändern tatsächlich das Carry-Flag. So haben z.&amp;amp;nbsp;B. die Inkrementier- und Dekrementierbefehle keine Auswirkung auf dieses Flag.&lt;br /&gt;
&lt;br /&gt;
===Zero (Z)===&lt;br /&gt;
Das Zero-Flag hält fest, ob das Ergebnis der letzten 8-Bit-Berechnung gleich 0 war oder nicht.&lt;br /&gt;
&lt;br /&gt;
===Negative (N)===&lt;br /&gt;
Spiegelt den Zustand des höchstwertigen Bits (Bit 7) der letzten 8-Bit-Berechnung wider. In Zweierkomplement-Arithmetik — Standard bei allen Compilern für Ganzzahlen — bedeutet ein gesetztes Bit 7 eine negative Zahl. Das Bit kann also dazu genutzt werden um festzustellen, ob das Ergebnis einer Berechnung im Sinne einer Zweierkomplement-Arithmetik positiv oder negativ ist.&lt;br /&gt;
&lt;br /&gt;
===Overflow (V)===&lt;br /&gt;
Dieses Bit wird gesetzt, wenn bei einer Berechnung mit 2er-Komplement-Arithmetik ein Überlauf (Unterlauf) stattgefunden hat. Dies entspricht einem Überlauf von Bit 6 ins Bit 7.&lt;br /&gt;
&lt;br /&gt;
Der Übertrag, der bei der Addition/Subtraktion von Bit 6 auf Bit 7 auftritt, zeigt daher – wenn er vorhanden ist – an,&lt;br /&gt;
dass es sich hier um einen Überlauf (Overflow) des Zahlenbereichs handelt und das Ergebnis falsch ist.&lt;br /&gt;
Das ist allerdings nicht der Fall, wenn auch der Übertrag von Bit 7 nach Bit 8 (Carry) aufgetreten ist.&lt;br /&gt;
Daher ist das Overflow-Flag die [[AVR-Tutorial: Logik#XOR (Exklusives Oder)|XOR-Verknüpfung]] aus dem Übertrag von Bit 6 nach Bit 7 und dem Carry.&lt;br /&gt;
&lt;br /&gt;
Beispiele für die Anwendung des V-Flags finden sich in [[AVR Arithmetik/Saturierung|saturierter Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===Signed (S)===&lt;br /&gt;
Das Signed-Bit ergibt sich aus der Antivalenz (exklusives Oder) der Flags N und V, also S = N XOR V.&lt;br /&gt;
Mit Hilfe des Signed-Flags können vorzeichenbehaftete Werte miteinander verglichen werden. Ist nach einem Vergleich zweier Register S=1, so ist der Wert des ersten Registers kleiner dem zweiten (in der Signed-Darstellung). Damit entspricht das Signed-Flag gewissermaßen dem Carry-Flag für Signed-Werte. Es wird hauptsächlich für „Signed“-Tests benötigt. Daher auch der Name.&lt;br /&gt;
&lt;br /&gt;
===Half Carry (H)===&lt;br /&gt;
Das Half-Carry-Flag hat die gleiche Aufgabe wie das Carry Flag, nur beschäftigt es sich mit einem Überlauf von Bit 3 nach Bit 4, also dem Übertrag zwischen dem oberen und dem unteren Nibble. Wie beim Carry-Flag gilt, dass das Flag nicht durch Inkrementieren bzw. Dekrementieren ausgelöst werden kann.&lt;br /&gt;
Das Haupteinsatzgebiet ist der Bereich der BCD-Arithmetik (binär codierte Dezimalzahlen), bei der jeweils 4 Bits eine Stelle einer Dezimalzahl repräsentieren.&lt;br /&gt;
&lt;br /&gt;
Kein Compiler benutzt das H-Flag. Findet man die Befehle &amp;lt;tt&amp;gt;brhc&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;brhs&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;clh&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;seh&amp;lt;/tt&amp;gt; in einem Disassemblerlisting, sind das entweder Daten oder es wurde in Assembler programmiert. Das H-Flag lässt sich bspw. dazu verwenden, zwei Zähler in einem Byte zu realisieren. So signalisiert das H-Flag in einem &amp;lt;u&amp;gt;Bit&amp;lt;/u&amp;gt;zähler, wann das nächste &amp;lt;u&amp;gt;Byte&amp;lt;/u&amp;gt; nachzuladen ist, indem man 2 addiert. Das reicht für 127 Bit.&lt;br /&gt;
&lt;br /&gt;
=== Transfer (T)===&lt;br /&gt;
Das T-Flag ist kein Statusbit im eigentlichen Sinne. Es steht dem Programmierer als 1-Bit-Speicher zur Verfügung. Der Zugriff erfolgt über die Befehle Bit Load (BLD), Bit Store (BST), Set-T (SET) und Clear-T (CLT) und wird sonst von keinen anderen Befehlen beeinflusst. Damit können Bits von einer Stelle schnell an eine andere kopiert oder getestet werden. Es gibt &amp;lt;i&amp;gt;keinen&amp;lt;/i&amp;gt; Befehl um das Bit zu kippen.&lt;br /&gt;
&lt;br /&gt;
Kein Compiler benutzt das T-Flag. Es kann jedoch in der Laufzeitbibliothek Verwendung finden, solche Kode-Abschnitte sind handoptimierter Assembler. &amp;lt;tt&amp;gt;avr-gcc&amp;lt;/tt&amp;gt; betrachtet das T-Flag als „flüchtig“ und darf in Unterprogrammen und Makros verändert werden.&lt;br /&gt;
&lt;br /&gt;
===Interrupt (I)===&lt;br /&gt;
Das Interrupt-Flag fällt hier etwas aus dem Rahmen; es hat nichts mit Berechnungen zu tun, sondern steuert, ob Interrupts im Controller zugelassen sind (siehe [[AVR-Tutorial: Interrupts]]).&lt;br /&gt;
&lt;br /&gt;
==Vergleiche==&lt;br /&gt;
Um einen Vergleich durchzuführen, wird intern eine Subtraktion der beiden Operanden vorgenommen. Das eigentliche Ergebnis der Subtraktion wird allerdings verworfen, es bleibt nur die neue Belegung der Flags übrig, die in weiterer Folge ausgewertet werden kann.&lt;br /&gt;
&lt;br /&gt;
===CP – Compare===&lt;br /&gt;
Vergleicht den Inhalt zweier Register miteinander. Prozessorintern wird dabei eine Subtraktion der beiden Register durchgeführt. Das eigentliche Subtraktionsergebnis wird allerdings verworfen, das Subtraktionsergebnis beeinflusst lediglich die Flags.&lt;br /&gt;
&lt;br /&gt;
===CPC – Compare with Carry===&lt;br /&gt;
Vergleicht den Inhalt zweier Register, wobei das Carry-Flag in den Vergleich mit einbezogen wird. Dieser Befehl wird für Arithmetik mit großen Variablen (16 oder 32 Bit) benötigt. Siehe [[AVR-Tutorial: Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===CPI – Compare Immediate===&lt;br /&gt;
Vergleicht den Inhalt eines Registers mit einer direkt angegebenen Konstanten. Der Befehl ist nur auf die Register r16…r31 anwendbar.&lt;br /&gt;
&lt;br /&gt;
==Bedingte Sprünge==&lt;br /&gt;
&lt;br /&gt;
Die bedingten Sprünge werten immer bestimmte Flags im Statusregister (SREG) aus. Es spielt dabei keine Rolle, ob dies nach einem Vergleichsbefehl oder einem sonstigen Befehl gemacht wird. Entscheidend ist einzig und allein der Zustand des abgefragten Flags. Die Namen der Sprungbefehle wurden allerdings so gewählt, daß sich im Befehlsnamen die Beziehung der Operanden direkt nach einem Compare-Befehl widerspiegelt. Zu beachten ist auch, daß die Flags nicht nur durch Vergleichsbefehle verändert werden, sondern auch durch arithmetische Operationen, Schiebebefehle und [[Bitmanipulation|logische Verknüpfungen]]. Da diese Information wichtig ist, ist auch in der bei Atmel/Microchip erhältlichen [https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf Übersicht über alle Assemblerbefehle] bei jedem Befehl angegeben, ob und wie er Flags beeinflusst. Ebenso ist dort eine kompakte Übersicht aller bedingten Sprünge zu finden. Beachten muss man jedoch, dass die bedingten Sprünge maximal 64 Worte weit springen können.&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenlose Zahlen ===&lt;br /&gt;
&lt;br /&gt;
;BRSH – Branch if Same or Higher: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) nicht gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist. BRSH ist identisch mit BRCC (Branch if Carry Cleared).&lt;br /&gt;
&lt;br /&gt;
;BRLO – Branch if Lower: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand kleiner dem zweiten Operanden ist. BRLO ist identisch mit BRCS (Branch if Carry Set).&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenbehaftete Zahlen ===&lt;br /&gt;
&lt;br /&gt;
;BRGE – Branch if Greater or Equal: Der Sprung wird durchgeführt, wenn das Signed-Flag (S) nicht gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist.&lt;br /&gt;
&lt;br /&gt;
;BRLT – Branch if Less Than: Der Sprung wird durchgeführt, wenn das Signed-Flag (S) gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der erste Operand kleiner als der zweite Operand ist.&lt;br /&gt;
&lt;br /&gt;
;BRMI – Branch if Minus: Der Sprung wird durchgeführt, wenn das Negativ-Flag (N) gesetzt ist, das Ergebnis der letzten Operation also negativ war.&lt;br /&gt;
&lt;br /&gt;
;BRPL – Branch if Plus: Der Sprung wird durchgeführt, wenn das Negativ-Flag (N) nicht gesetzt ist, das Ergebnis der letzten Operation also positiv war (einschließlich null).&lt;br /&gt;
&lt;br /&gt;
=== Sonstige bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
;BREQ – Branch if Equal: Der Sprung wird durchgeführt, wenn das Zero-Flag (Z) gesetzt ist. Ist nach einem Vergleich das Zero-Flag gesetzt, lieferte die interne Subtraktion also 0, so waren beide Operanden gleich.&lt;br /&gt;
&lt;br /&gt;
;BRNE – Branch if Not Equal: Der Sprung wird durchgeführt, wenn das Zero-Flag (Z) nicht gesetzt ist. Ist nach einem Vergleich das Zero-Flag nicht gesetzt, lieferte die interne Subtraktion also nicht 0, so waren beide Operanden verschieden.&lt;br /&gt;
&lt;br /&gt;
;BRCC – Branch if Carry Flag is Cleared: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) nicht gesetzt ist. Dieser Befehl wird oft für Arithmetik mit großen Variablen (16 oder 32&amp;amp;nbsp;Bit) bzw. im Zusammenhang mit Schiebeoperationen verwendet. BRCC ≡ BRSH&lt;br /&gt;
&lt;br /&gt;
;BRCS – Branch if Carry Flag is Set: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) gesetzt ist. Die Verwendung ist sehr ähnlich zu BRCC. BRCS ≡ BRLO&lt;br /&gt;
&lt;br /&gt;
=== Selten verwendete bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
;BRHC – Branch if Half Carry Flag is Cleared: Der Sprung wird durchgeführt, wenn das Half-Carry-Flag (H) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRHS – Branch if Half Carry Flag is Set: Der Sprung wird durchgeführt, wenn das Half-Carry-Flag (H) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRID – Branch if Global Interrupt is Disabled: Der Sprung wird durchgeführt, wenn das Interrupt-Flag (I) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRIE – Branch if Global Interrupt is Enabled: Der Sprung wird durchgeführt, wenn das Interrupt-Flag (I) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRTC – Branch if T Flag is Cleared: Der Sprung wird durchgeführt, wenn das T-Flag nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRTS – Branch if T Flag is Set: Der Sprung wird durchgeführt, wenn das T-Flag gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRVC – Branch if Overflow Cleared: Der Sprung wird durchgeführt, wenn das Overflow-Flag (V) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRVS – Branch if Overflow Set: Der Sprung wird durchgeführt, wenn das Overflow-Flag (V) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
==Beispiele==&lt;br /&gt;
&lt;br /&gt;
=== Entscheidungen ===&lt;br /&gt;
&lt;br /&gt;
In jedem Programm kommt früher oder später das Problem, die Ausführung von Codeteilen von irgendwelchen Zahlenwerten, die sich in anderen Registern befinden, abhängig zu machen. Sieht beispielsweise die Aufgabe vor, daß Register r18 auf 0 gesetzt werden soll, falls im Register r17 der Zahlenwert 25 enthalten ist und in allen anderen Fällen soll r18 auf 123 gesetzt werden, dann lautet der Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    cpi     r17, 25         ; vergleiche r17 mit der Konstante 25&lt;br /&gt;
    brne    nicht_gleich    ; wenn nicht gleich, dann mach bei nicht_gleich weiter&lt;br /&gt;
    ldi     r18, 0          ; hier stehen nun Anweisungen für den Fall,&lt;br /&gt;
                            ; dass R17 gleich 25 ist&lt;br /&gt;
    rjmp    weiter          ; meist will man den anderen Zweig nicht durchlaufen, darum der Sprung&lt;br /&gt;
nicht_gleich:&lt;br /&gt;
    ldi     r18, 123        ; hier stehen nun Anweisungen für den Fall,&lt;br /&gt;
                            ; dass R17 ungleich 25 ist&lt;br /&gt;
weiter:                     ; hier geht das Programm weiter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In ähnlicher Weise können die anderen bedingten Sprungbefehle eingesetzt werden, um die üblicherweise vorkommenden Vergleiche auf Gleichheit, Ungleichheit, „größer als“, „kleiner als“ zu realisieren.&lt;br /&gt;
&lt;br /&gt;
===Schleifenkonstrukte===&lt;br /&gt;
&lt;br /&gt;
Ein immer wiederkehrendes Muster in der Programmierung ist eine &#039;&#039;&#039;Schleife&#039;&#039;&#039;. Die einfachste Form einer Schleife ist die &#039;&#039;Zählschleife&#039;&#039;. Dabei wird ein Register von einem Startwert ausgehend eine gewisse Anzahl erhöht, bis ein Endwert erreicht wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    ldi     r17, 10         ; der Startwert sei in diesem Beispiel 10&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    inc     r17             ; erhöhe das Zählregister&lt;br /&gt;
    cpi     r17, 134        ; mit dem Endwert vergleichen&lt;br /&gt;
    brne    loop            ; und wenn der Endwert noch nicht erreicht ist,&lt;br /&gt;
                            ; wird bei der Marke loop ein weiterer Schleifendurchlauf ausgeführt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sehr oft ist es auch möglich, das Konstrukt umzukehren. Anstatt von einem Startwert aus zu inkrementieren genügt es, die Anzahl der gewünschten Schleifendurchläufe in ein Register zu laden und dieses Register zu dekrementieren. Dabei kann man von der Eigenschaft der Dekrementieranweisung Gebrauch machen, das Zero-Flag (Z) zu beeinflussen. Ist das Ergebnis des Dekrements gleich 0, so wird das Zero-Flag gesetzt, welches wiederum in der nachfolgenden BRNE-Anweisung für einen bedingten Sprung benutzt werden kann. Das vereinfacht die Schleife und spart eine Anweisung sowie einen Takt Ausführungszeit.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    ldi     r17, 124        ; Die Anzahl der Wiederholungen in ein Register laden&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    dec     r17             ; Schleifenzähler um 1 verringern, dabei wird das Zero-Flag beeinflusst&lt;br /&gt;
    brne    loop            ; wenn r17 noch nicht 0 geworden ist -&amp;gt; Schleife wiederholen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Literatur==&lt;br /&gt;
* [https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf AVR Instruction Set Manual]: Übersicht aller AVR-Befehle mit Angaben zur Beeinflussung der Statusbits&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Interrupts|&lt;br /&gt;
zurücklink=AVR-Tutorial: Interrupts|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Mehrfachverzweigungen|&lt;br /&gt;
vorlink=AVR-Tutorial: Mehrfachverzweigung}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Vergleiche]]&lt;br /&gt;
[[Category:AVR-Arithmetik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=107247</id>
		<title>AVR-Tutorial: Vergleiche</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=107247"/>
		<updated>2025-01-27T12:50:13Z</updated>

		<summary type="html">&lt;p&gt;Heha: Bit Transfer (T)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Vergleiche und Entscheidungen sind in jeder Programmiersprache ein zentrales Mittel, um den Programmfluss abhängig von Bedingungen zu kontrollieren. In einem [[AVR]] spielen dazu vier Komponenten zusammen:&lt;br /&gt;
* Vergleichsbefehle,&lt;br /&gt;
* die Flags im Statusregister,&lt;br /&gt;
* bedingte Sprungbefehle,&lt;br /&gt;
* andere Befehle, die die Flags im Statusregister beeinflussen, wie z.&amp;amp;nbsp;B. die meisten arithmetischen Funktionen.&lt;br /&gt;
&lt;br /&gt;
Der Zusammenhang ist dabei folgender: Die Vergleichsbefehle führen einen Vergleich durch, zum Beispiel zwischen zwei Registern oder zwischen einem Register und einer Konstanten. Das Ergebnis des Vergleiches wird in den Flags abgelegt. Die bedingten Sprungbefehle werten die Flags aus und führen bei einem positiven Ergebnis den Sprung aus. Besonders der erste Satzteil ist wichtig! Den bedingten Sprungbefehlen ist es nämlich völlig egal, ob die Flags über Vergleichsbefehle oder über sonstige Befehle gesetzt wurden. Die Sprungbefehle werten einfach nur die Flags aus, wie auch immer diese zu ihrem Zustand kommen.&lt;br /&gt;
&lt;br /&gt;
==Flags==&lt;br /&gt;
&lt;br /&gt;
Die Flags sind Bits im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;. Ihre Aufgabe ist es, das Auftreten bestimmter Ereignisse, die während Berechnungen eintreten können, festzuhalten.&lt;br /&gt;
Speicherbefehle (LD, LDI, ST, MOV, …) haben auf dem AVR grundsätzlich keinen Einfluss auf das Statusregister. Will man den Inhalt eines Registers explizit testen (z.&amp;amp;nbsp;B. nach dem Laden aus dem SRAM), so kann man hierfür den TST-Befehl verwenden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | I | T | H | S | V | N | Z | C |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{{Byte|Bits im SREG| I|T|H|S|V|N|Z|C}}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Carry (C)===&lt;br /&gt;
Das Carry-Flag hält fest, ob es bei der letzten Berechnung einen Über- oder Unterlauf gab. Aber Achtung: Nicht alle arithmetischen Befehle verändern tatsächlich das Carry-Flag. So haben z.&amp;amp;nbsp;B. die Inkrementier- und Dekrementierbefehle keine Auswirkung auf dieses Flag.&lt;br /&gt;
&lt;br /&gt;
===Zero (Z)===&lt;br /&gt;
Das Zero-Flag hält fest, ob das Ergebnis der letzten 8-Bit-Berechnung gleich 0 war oder nicht.&lt;br /&gt;
&lt;br /&gt;
===Negative (N)===&lt;br /&gt;
Spiegelt den Zustand des höchstwertigen Bits (Bit 7) der letzten 8-Bit-Berechnung wider. In 2er-Komplement-Arithmetik bedeutet ein gesetztes Bit 7 eine negative Zahl, das Bit kann also dazu genutzt werden um festzustellen, ob das Ergebnis einer Berechnung im Sinne einer 2er-Komplement-Arithmetik positiv oder negativ ist.&lt;br /&gt;
&lt;br /&gt;
===Overflow (V)===&lt;br /&gt;
Dieses Bit wird gesetzt, wenn bei einer Berechnung mit 2er-Komplement-Arithmetik ein Überlauf (Unterlauf) stattgefunden hat. Dies entspricht einem Überlauf von Bit 6 ins Bit 7.&lt;br /&gt;
&lt;br /&gt;
Der Übertrag, der bei der Addition/Subtraktion von Bit 6 auf Bit 7 auftritt, zeigt daher – wenn er vorhanden ist – an,&lt;br /&gt;
dass es sich hier um einen Überlauf (Overflow) des Zahlenbereichs handelt und das Ergebnis falsch ist.&lt;br /&gt;
Das ist allerdings nicht der Fall, wenn auch der Übertrag von Bit 7 nach Bit 8 (Carry) aufgetreten ist.&lt;br /&gt;
Daher ist das Overflow-Flag die [[AVR-Tutorial: Logik#XOR (Exklusives Oder)|XOR-Verknüpfung]] aus dem Übertrag von Bit 6 nach Bit 7 und dem Carry.&lt;br /&gt;
&lt;br /&gt;
Beispiele für die Anwendung des V-Flags finden sich in [[AVR Arithmetik/Saturierung|saturierter Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===Signed (S)===&lt;br /&gt;
Das Signed-Bit ergibt sich aus der Antivalenz (exklusives Oder) der Flags N und V, also S = N XOR V.&lt;br /&gt;
Mit Hilfe des Signed-Flags können vorzeichenbehaftete Werte miteinander verglichen werden. Ist nach einem Vergleich zweier Register S=1, so ist der Wert des ersten Registers kleiner dem zweiten (in der Signed-Darstellung). Damit entspricht das Signed-Flag gewissermaßen dem Carry-Flag für Signed-Werte. Es wird hauptsächlich für „Signed“-Tests benötigt. Daher auch der Name.&lt;br /&gt;
&lt;br /&gt;
===Half Carry (H)===&lt;br /&gt;
Das Half-Carry-Flag hat die gleiche Aufgabe wie das Carry Flag, nur beschäftigt es sich mit einem Überlauf von Bit 3 nach Bit 4, also dem Übertrag zwischen dem oberen und dem unteren Nibble. Wie beim Carry-Flag gilt, dass das Flag nicht durch Inkrementieren bzw. Dekrementieren ausgelöst werden kann.&lt;br /&gt;
Das Haupteinsatzgebiet ist der Bereich der BCD-Arithmetik (binär codierte Dezimalzahlen), bei der jeweils 4 Bits eine Stelle einer Dezimalzahl repräsentieren.&lt;br /&gt;
&lt;br /&gt;
Kein Compiler benutzt das H-Flag. Findet man die Befehle &amp;lt;tt&amp;gt;brhc&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;brhs&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;clh&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;seh&amp;lt;/tt&amp;gt; in einem Disassemblerlisting, sind das entweder Daten oder es wurde in Assembler programmiert. Das H-Flag lässt sich bspw. dazu verwenden, zwei Zähler in einem Byte zu realisieren. So signalisiert das H-Flag in einem &amp;lt;u&amp;gt;Bit&amp;lt;/u&amp;gt;zähler, wann das nächste &amp;lt;u&amp;gt;Byte&amp;lt;/u&amp;gt; nachzuladen ist, indem man 2 addiert. Das reicht für 127 Bit.&lt;br /&gt;
&lt;br /&gt;
=== Transfer (T)===&lt;br /&gt;
Das T-Flag ist kein Statusbit im eigentlichen Sinne. Es steht dem Programmierer als 1-Bit-Speicher zur Verfügung. Der Zugriff erfolgt über die Befehle Bit Load (BLD), Bit Store (BST), Set-T (SET) und Clear-T (CLT) und wird sonst von keinen anderen Befehlen beeinflusst. Damit können Bits von einer Stelle schnell an eine andere kopiert oder getestet werden. Es gibt &amp;lt;i&amp;gt;keinen&amp;lt;/i&amp;gt; Befehl um das Bit zu kippen.&lt;br /&gt;
&lt;br /&gt;
Kein Compiler benutzt das T-Flag. Es kann jedoch in der Laufzeitbibliothek Verwendung finden, solche Kode-Abschnitte sind handoptimierter Assembler. &amp;lt;tt&amp;gt;avr-gcc&amp;lt;/tt&amp;gt; betrachtet das T-Flag als „flüchtig“ und darf in Unterprogrammen und Makros verändert werden.&lt;br /&gt;
&lt;br /&gt;
===Interrupt (I)===&lt;br /&gt;
Das Interrupt-Flag fällt hier etwas aus dem Rahmen; es hat nichts mit Berechnungen zu tun, sondern steuert, ob Interrupts im Controller zugelassen sind (siehe [[AVR-Tutorial: Interrupts]]).&lt;br /&gt;
&lt;br /&gt;
==Vergleiche==&lt;br /&gt;
Um einen Vergleich durchzuführen, wird intern eine Subtraktion der beiden Operanden vorgenommen. Das eigentliche Ergebnis der Subtraktion wird allerdings verworfen, es bleibt nur die neue Belegung der Flags übrig, die in weiterer Folge ausgewertet werden kann.&lt;br /&gt;
&lt;br /&gt;
===CP – Compare===&lt;br /&gt;
Vergleicht den Inhalt zweier Register miteinander. Prozessorintern wird dabei eine Subtraktion der beiden Register durchgeführt. Das eigentliche Subtraktionsergebnis wird allerdings verworfen, das Subtraktionsergebnis beeinflusst lediglich die Flags.&lt;br /&gt;
&lt;br /&gt;
===CPC – Compare with Carry===&lt;br /&gt;
Vergleicht den Inhalt zweier Register, wobei das Carry-Flag in den Vergleich mit einbezogen wird. Dieser Befehl wird für Arithmetik mit großen Variablen (16 oder 32 Bit) benötigt. Siehe [[AVR-Tutorial: Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===CPI – Compare Immediate===&lt;br /&gt;
Vergleicht den Inhalt eines Registers mit einer direkt angegebenen Konstanten. Der Befehl ist nur auf die Register r16…r31 anwendbar.&lt;br /&gt;
&lt;br /&gt;
==Bedingte Sprünge==&lt;br /&gt;
&lt;br /&gt;
Die bedingten Sprünge werten immer bestimmte Flags im Statusregister (SREG) aus. Es spielt dabei keine Rolle, ob dies nach einem Vergleichsbefehl oder einem sonstigen Befehl gemacht wird. Entscheidend ist einzig und allein der Zustand des abgefragten Flags. Die Namen der Sprungbefehle wurden allerdings so gewählt, daß sich im Befehlsnamen die Beziehung der Operanden direkt nach einem Compare-Befehl widerspiegelt. Zu beachten ist auch, daß die Flags nicht nur durch Vergleichsbefehle verändert werden, sondern auch durch arithmetische Operationen, Schiebebefehle und [[Bitmanipulation|logische Verknüpfungen]]. Da diese Information wichtig ist, ist auch in der bei Atmel/Microchip erhältlichen [https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf Übersicht über alle Assemblerbefehle] bei jedem Befehl angegeben, ob und wie er Flags beeinflusst. Ebenso ist dort eine kompakte Übersicht aller bedingten Sprünge zu finden. Beachten muss man jedoch, dass die bedingten Sprünge maximal 64 Worte weit springen können.&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenlose Zahlen ===&lt;br /&gt;
&lt;br /&gt;
;BRSH – Branch if Same or Higher: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) nicht gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist. BRSH ist identisch mit BRCC (Branch if Carry Cleared).&lt;br /&gt;
&lt;br /&gt;
;BRLO – Branch if Lower: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand kleiner dem zweiten Operanden ist. BRLO ist identisch mit BRCS (Branch if Carry Set).&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenbehaftete Zahlen ===&lt;br /&gt;
&lt;br /&gt;
;BRGE – Branch if Greater or Equal: Der Sprung wird durchgeführt, wenn das Signed-Flag (S) nicht gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist.&lt;br /&gt;
&lt;br /&gt;
;BRLT – Branch if Less Than: Der Sprung wird durchgeführt, wenn das Signed-Flag (S) gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der erste Operand kleiner als der zweite Operand ist.&lt;br /&gt;
&lt;br /&gt;
;BRMI – Branch if Minus: Der Sprung wird durchgeführt, wenn das Negativ-Flag (N) gesetzt ist, das Ergebnis der letzten Operation also negativ war.&lt;br /&gt;
&lt;br /&gt;
;BRPL – Branch if Plus: Der Sprung wird durchgeführt, wenn das Negativ-Flag (N) nicht gesetzt ist, das Ergebnis der letzten Operation also positiv war (einschließlich null).&lt;br /&gt;
&lt;br /&gt;
=== Sonstige bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
;BREQ – Branch if Equal: Der Sprung wird durchgeführt, wenn das Zero-Flag (Z) gesetzt ist. Ist nach einem Vergleich das Zero-Flag gesetzt, lieferte die interne Subtraktion also 0, so waren beide Operanden gleich.&lt;br /&gt;
&lt;br /&gt;
;BRNE – Branch if Not Equal: Der Sprung wird durchgeführt, wenn das Zero-Flag (Z) nicht gesetzt ist. Ist nach einem Vergleich das Zero-Flag nicht gesetzt, lieferte die interne Subtraktion also nicht 0, so waren beide Operanden verschieden.&lt;br /&gt;
&lt;br /&gt;
;BRCC – Branch if Carry Flag is Cleared: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) nicht gesetzt ist. Dieser Befehl wird oft für Arithmetik mit großen Variablen (16 oder 32&amp;amp;nbsp;Bit) bzw. im Zusammenhang mit Schiebeoperationen verwendet. BRCC ≡ BRSH&lt;br /&gt;
&lt;br /&gt;
;BRCS – Branch if Carry Flag is Set: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) gesetzt ist. Die Verwendung ist sehr ähnlich zu BRCC. BRCS ≡ BRLO&lt;br /&gt;
&lt;br /&gt;
=== Selten verwendete bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
;BRHC – Branch if Half Carry Flag is Cleared: Der Sprung wird durchgeführt, wenn das Half-Carry-Flag (H) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRHS – Branch if Half Carry Flag is Set: Der Sprung wird durchgeführt, wenn das Half-Carry-Flag (H) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRID – Branch if Global Interrupt is Disabled: Der Sprung wird durchgeführt, wenn das Interrupt-Flag (I) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRIE – Branch if Global Interrupt is Enabled: Der Sprung wird durchgeführt, wenn das Interrupt-Flag (I) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRTC – Branch if T Flag is Cleared: Der Sprung wird durchgeführt, wenn das T-Flag nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRTS – Branch if T Flag is Set: Der Sprung wird durchgeführt, wenn das T-Flag gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRVC – Branch if Overflow Cleared: Der Sprung wird durchgeführt, wenn das Overflow-Flag (V) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRVS – Branch if Overflow Set: Der Sprung wird durchgeführt, wenn das Overflow-Flag (V) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
==Beispiele==&lt;br /&gt;
&lt;br /&gt;
=== Entscheidungen ===&lt;br /&gt;
&lt;br /&gt;
In jedem Programm kommt früher oder später das Problem, die Ausführung von Codeteilen von irgendwelchen Zahlenwerten, die sich in anderen Registern befinden, abhängig zu machen. Sieht beispielsweise die Aufgabe vor, daß Register r18 auf 0 gesetzt werden soll, falls im Register r17 der Zahlenwert 25 enthalten ist und in allen anderen Fällen soll r18 auf 123 gesetzt werden, dann lautet der Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    cpi     r17, 25         ; vergleiche r17 mit der Konstante 25&lt;br /&gt;
    brne    nicht_gleich    ; wenn nicht gleich, dann mach bei nicht_gleich weiter&lt;br /&gt;
    ldi     r18, 0          ; hier stehen nun Anweisungen für den Fall,&lt;br /&gt;
                            ; dass R17 gleich 25 ist&lt;br /&gt;
    rjmp    weiter          ; meist will man den anderen Zweig nicht durchlaufen, darum der Sprung&lt;br /&gt;
nicht_gleich:&lt;br /&gt;
    ldi     r18, 123        ; hier stehen nun Anweisungen für den Fall,&lt;br /&gt;
                            ; dass R17 ungleich 25 ist&lt;br /&gt;
weiter:                     ; hier geht das Programm weiter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In ähnlicher Weise können die anderen bedingten Sprungbefehle eingesetzt werden, um die üblicherweise vorkommenden Vergleiche auf Gleichheit, Ungleichheit, „größer als“, „kleiner als“ zu realisieren.&lt;br /&gt;
&lt;br /&gt;
===Schleifenkonstrukte===&lt;br /&gt;
&lt;br /&gt;
Ein immer wiederkehrendes Muster in der Programmierung ist eine &#039;&#039;&#039;Schleife&#039;&#039;&#039;. Die einfachste Form einer Schleife ist die &#039;&#039;Zählschleife&#039;&#039;. Dabei wird ein Register von einem Startwert ausgehend eine gewisse Anzahl erhöht, bis ein Endwert erreicht wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    ldi     r17, 10         ; der Startwert sei in diesem Beispiel 10&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    inc     r17             ; erhöhe das Zählregister&lt;br /&gt;
    cpi     r17, 134        ; mit dem Endwert vergleichen&lt;br /&gt;
    brne    loop            ; und wenn der Endwert noch nicht erreicht ist,&lt;br /&gt;
                            ; wird bei der Marke loop ein weiterer Schleifendurchlauf ausgeführt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sehr oft ist es auch möglich, das Konstrukt umzukehren. Anstatt von einem Startwert aus zu inkrementieren genügt es, die Anzahl der gewünschten Schleifendurchläufe in ein Register zu laden und dieses Register zu dekrementieren. Dabei kann man von der Eigenschaft der Dekrementieranweisung Gebrauch machen, das Zero-Flag (Z) zu beeinflussen. Ist das Ergebnis des Dekrements gleich 0, so wird das Zero-Flag gesetzt, welches wiederum in der nachfolgenden BRNE-Anweisung für einen bedingten Sprung benutzt werden kann. Das vereinfacht die Schleife und spart eine Anweisung sowie einen Takt Ausführungszeit.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    ldi     r17, 124        ; Die Anzahl der Wiederholungen in ein Register laden&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    dec     r17             ; Schleifenzähler um 1 verringern, dabei wird das Zero-Flag beeinflusst&lt;br /&gt;
    brne    loop            ; wenn r17 noch nicht 0 geworden ist -&amp;gt; Schleife wiederholen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Literatur==&lt;br /&gt;
* [https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf AVR Instruction Set Manual]: Übersicht aller AVR-Befehle mit Angaben zur Beeinflussung der Statusbits&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Interrupts|&lt;br /&gt;
zurücklink=AVR-Tutorial: Interrupts|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Mehrfachverzweigungen|&lt;br /&gt;
vorlink=AVR-Tutorial: Mehrfachverzweigung}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Vergleiche]]&lt;br /&gt;
[[Category:AVR-Arithmetik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=107246</id>
		<title>AVR-Tutorial: Vergleiche</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=107246"/>
		<updated>2025-01-27T12:43:19Z</updated>

		<summary type="html">&lt;p&gt;Heha: Half Carry (H)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Vergleiche und Entscheidungen sind in jeder Programmiersprache ein zentrales Mittel, um den Programmfluss abhängig von Bedingungen zu kontrollieren. In einem [[AVR]] spielen dazu vier Komponenten zusammen:&lt;br /&gt;
* Vergleichsbefehle,&lt;br /&gt;
* die Flags im Statusregister,&lt;br /&gt;
* bedingte Sprungbefehle,&lt;br /&gt;
* andere Befehle, die die Flags im Statusregister beeinflussen, wie z.&amp;amp;nbsp;B. die meisten arithmetischen Funktionen.&lt;br /&gt;
&lt;br /&gt;
Der Zusammenhang ist dabei folgender: Die Vergleichsbefehle führen einen Vergleich durch, zum Beispiel zwischen zwei Registern oder zwischen einem Register und einer Konstanten. Das Ergebnis des Vergleiches wird in den Flags abgelegt. Die bedingten Sprungbefehle werten die Flags aus und führen bei einem positiven Ergebnis den Sprung aus. Besonders der erste Satzteil ist wichtig! Den bedingten Sprungbefehlen ist es nämlich völlig egal, ob die Flags über Vergleichsbefehle oder über sonstige Befehle gesetzt wurden. Die Sprungbefehle werten einfach nur die Flags aus, wie auch immer diese zu ihrem Zustand kommen.&lt;br /&gt;
&lt;br /&gt;
==Flags==&lt;br /&gt;
&lt;br /&gt;
Die Flags sind Bits im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;. Ihre Aufgabe ist es, das Auftreten bestimmter Ereignisse, die während Berechnungen eintreten können, festzuhalten.&lt;br /&gt;
Speicherbefehle (LD, LDI, ST, MOV, …) haben auf dem AVR grundsätzlich keinen Einfluss auf das Statusregister. Will man den Inhalt eines Registers explizit testen (z.&amp;amp;nbsp;B. nach dem Laden aus dem SRAM), so kann man hierfür den TST-Befehl verwenden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | I | T | H | S | V | N | Z | C |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{{Byte|Bits im SREG| I|T|H|S|V|N|Z|C}}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Carry (C)===&lt;br /&gt;
Das Carry-Flag hält fest, ob es bei der letzten Berechnung einen Über- oder Unterlauf gab. Aber Achtung: Nicht alle arithmetischen Befehle verändern tatsächlich das Carry-Flag. So haben z.&amp;amp;nbsp;B. die Inkrementier- und Dekrementierbefehle keine Auswirkung auf dieses Flag.&lt;br /&gt;
&lt;br /&gt;
===Zero (Z)===&lt;br /&gt;
Das Zero-Flag hält fest, ob das Ergebnis der letzten 8-Bit-Berechnung gleich 0 war oder nicht.&lt;br /&gt;
&lt;br /&gt;
===Negative (N)===&lt;br /&gt;
Spiegelt den Zustand des höchstwertigen Bits (Bit 7) der letzten 8-Bit-Berechnung wider. In 2er-Komplement-Arithmetik bedeutet ein gesetztes Bit 7 eine negative Zahl, das Bit kann also dazu genutzt werden um festzustellen, ob das Ergebnis einer Berechnung im Sinne einer 2er-Komplement-Arithmetik positiv oder negativ ist.&lt;br /&gt;
&lt;br /&gt;
===Overflow (V)===&lt;br /&gt;
Dieses Bit wird gesetzt, wenn bei einer Berechnung mit 2er-Komplement-Arithmetik ein Überlauf (Unterlauf) stattgefunden hat. Dies entspricht einem Überlauf von Bit 6 ins Bit 7.&lt;br /&gt;
&lt;br /&gt;
Der Übertrag, der bei der Addition/Subtraktion von Bit 6 auf Bit 7 auftritt, zeigt daher – wenn er vorhanden ist – an,&lt;br /&gt;
dass es sich hier um einen Überlauf (Overflow) des Zahlenbereichs handelt und das Ergebnis falsch ist.&lt;br /&gt;
Das ist allerdings nicht der Fall, wenn auch der Übertrag von Bit 7 nach Bit 8 (Carry) aufgetreten ist.&lt;br /&gt;
Daher ist das Overflow-Flag die [[AVR-Tutorial: Logik#XOR (Exklusives Oder)|XOR-Verknüpfung]] aus dem Übertrag von Bit 6 nach Bit 7 und dem Carry.&lt;br /&gt;
&lt;br /&gt;
Beispiele für die Anwendung des V-Flags finden sich in [[AVR Arithmetik/Saturierung|saturierter Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===Signed (S)===&lt;br /&gt;
Das Signed-Bit ergibt sich aus der Antivalenz (exklusives Oder) der Flags N und V, also S = N XOR V.&lt;br /&gt;
Mit Hilfe des Signed-Flags können vorzeichenbehaftete Werte miteinander verglichen werden. Ist nach einem Vergleich zweier Register S=1, so ist der Wert des ersten Registers kleiner dem zweiten (in der Signed-Darstellung). Damit entspricht das Signed-Flag gewissermaßen dem Carry-Flag für Signed-Werte. Es wird hauptsächlich für „Signed“-Tests benötigt. Daher auch der Name.&lt;br /&gt;
&lt;br /&gt;
===Half Carry (H)===&lt;br /&gt;
Das Half-Carry-Flag hat die gleiche Aufgabe wie das Carry Flag, nur beschäftigt es sich mit einem Überlauf von Bit 3 nach Bit 4, also dem Übertrag zwischen dem oberen und dem unteren Nibble. Wie beim Carry-Flag gilt, dass das Flag nicht durch Inkrementieren bzw. Dekrementieren ausgelöst werden kann.&lt;br /&gt;
Das Haupteinsatzgebiet ist der Bereich der BCD-Arithmetik (binär codierte Dezimalzahlen), bei der jeweils 4 Bits eine Stelle einer Dezimalzahl repräsentieren.&lt;br /&gt;
&lt;br /&gt;
Kein Compiler benutzt das H-Flag. Findet man die Befehle &amp;lt;tt&amp;gt;brhc&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;brhs&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;clh&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;seh&amp;lt;/tt&amp;gt; in einem Disassemblerlisting, sind das entweder Daten oder es wurde in Assembler programmiert. Das H-Flag lässt sich bspw. dazu verwenden, zwei Zähler in einem Byte zu realisieren. So signalisiert das H-Flag in einem &amp;lt;u&amp;gt;Bit&amp;lt;/u&amp;gt;zähler, wann das nächste &amp;lt;u&amp;gt;Byte&amp;lt;/u&amp;gt; nachzuladen ist, indem man 2 addiert. Das reicht für 127 Bit.&lt;br /&gt;
&lt;br /&gt;
=== Transfer (T)===&lt;br /&gt;
Das T-Flag ist kein Statusbit im eigentlichen Sinne. Es steht dem Programmierer als 1-Bit-Speicher zur Verfügung. Der Zugriff erfolgt über die Befehle Bit Load (BLD), Bit Store (BST), Set (SET) und Clear (CLT) und wird sonst von keinen anderen Befehlen beeinflusst. Damit können Bits von einer Stelle schnell an eine andere kopiert oder getestet werden.&lt;br /&gt;
&lt;br /&gt;
===Interrupt (I)===&lt;br /&gt;
Das Interrupt-Flag fällt hier etwas aus dem Rahmen; es hat nichts mit Berechnungen zu tun, sondern steuert, ob Interrupts im Controller zugelassen sind (siehe [[AVR-Tutorial: Interrupts]]).&lt;br /&gt;
&lt;br /&gt;
==Vergleiche==&lt;br /&gt;
Um einen Vergleich durchzuführen, wird intern eine Subtraktion der beiden Operanden vorgenommen. Das eigentliche Ergebnis der Subtraktion wird allerdings verworfen, es bleibt nur die neue Belegung der Flags übrig, die in weiterer Folge ausgewertet werden kann.&lt;br /&gt;
&lt;br /&gt;
===CP – Compare===&lt;br /&gt;
Vergleicht den Inhalt zweier Register miteinander. Prozessorintern wird dabei eine Subtraktion der beiden Register durchgeführt. Das eigentliche Subtraktionsergebnis wird allerdings verworfen, das Subtraktionsergebnis beeinflusst lediglich die Flags.&lt;br /&gt;
&lt;br /&gt;
===CPC – Compare with Carry===&lt;br /&gt;
Vergleicht den Inhalt zweier Register, wobei das Carry-Flag in den Vergleich mit einbezogen wird. Dieser Befehl wird für Arithmetik mit großen Variablen (16 oder 32 Bit) benötigt. Siehe [[AVR-Tutorial: Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===CPI – Compare Immediate===&lt;br /&gt;
Vergleicht den Inhalt eines Registers mit einer direkt angegebenen Konstanten. Der Befehl ist nur auf die Register r16…r31 anwendbar.&lt;br /&gt;
&lt;br /&gt;
==Bedingte Sprünge==&lt;br /&gt;
&lt;br /&gt;
Die bedingten Sprünge werten immer bestimmte Flags im Statusregister (SREG) aus. Es spielt dabei keine Rolle, ob dies nach einem Vergleichsbefehl oder einem sonstigen Befehl gemacht wird. Entscheidend ist einzig und allein der Zustand des abgefragten Flags. Die Namen der Sprungbefehle wurden allerdings so gewählt, daß sich im Befehlsnamen die Beziehung der Operanden direkt nach einem Compare-Befehl widerspiegelt. Zu beachten ist auch, daß die Flags nicht nur durch Vergleichsbefehle verändert werden, sondern auch durch arithmetische Operationen, Schiebebefehle und [[Bitmanipulation|logische Verknüpfungen]]. Da diese Information wichtig ist, ist auch in der bei Atmel/Microchip erhältlichen [https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf Übersicht über alle Assemblerbefehle] bei jedem Befehl angegeben, ob und wie er Flags beeinflusst. Ebenso ist dort eine kompakte Übersicht aller bedingten Sprünge zu finden. Beachten muss man jedoch, dass die bedingten Sprünge maximal 64 Worte weit springen können.&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenlose Zahlen ===&lt;br /&gt;
&lt;br /&gt;
;BRSH – Branch if Same or Higher: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) nicht gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist. BRSH ist identisch mit BRCC (Branch if Carry Cleared).&lt;br /&gt;
&lt;br /&gt;
;BRLO – Branch if Lower: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand kleiner dem zweiten Operanden ist. BRLO ist identisch mit BRCS (Branch if Carry Set).&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenbehaftete Zahlen ===&lt;br /&gt;
&lt;br /&gt;
;BRGE – Branch if Greater or Equal: Der Sprung wird durchgeführt, wenn das Signed-Flag (S) nicht gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist.&lt;br /&gt;
&lt;br /&gt;
;BRLT – Branch if Less Than: Der Sprung wird durchgeführt, wenn das Signed-Flag (S) gesetzt ist. Wird dieser Branch direkt nach einer CP-, CPI-, SUB- oder SUBI-Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der erste Operand kleiner als der zweite Operand ist.&lt;br /&gt;
&lt;br /&gt;
;BRMI – Branch if Minus: Der Sprung wird durchgeführt, wenn das Negativ-Flag (N) gesetzt ist, das Ergebnis der letzten Operation also negativ war.&lt;br /&gt;
&lt;br /&gt;
;BRPL – Branch if Plus: Der Sprung wird durchgeführt, wenn das Negativ-Flag (N) nicht gesetzt ist, das Ergebnis der letzten Operation also positiv war (einschließlich null).&lt;br /&gt;
&lt;br /&gt;
=== Sonstige bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
;BREQ – Branch if Equal: Der Sprung wird durchgeführt, wenn das Zero-Flag (Z) gesetzt ist. Ist nach einem Vergleich das Zero-Flag gesetzt, lieferte die interne Subtraktion also 0, so waren beide Operanden gleich.&lt;br /&gt;
&lt;br /&gt;
;BRNE – Branch if Not Equal: Der Sprung wird durchgeführt, wenn das Zero-Flag (Z) nicht gesetzt ist. Ist nach einem Vergleich das Zero-Flag nicht gesetzt, lieferte die interne Subtraktion also nicht 0, so waren beide Operanden verschieden.&lt;br /&gt;
&lt;br /&gt;
;BRCC – Branch if Carry Flag is Cleared: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) nicht gesetzt ist. Dieser Befehl wird oft für Arithmetik mit großen Variablen (16 oder 32&amp;amp;nbsp;Bit) bzw. im Zusammenhang mit Schiebeoperationen verwendet. BRCC ≡ BRSH&lt;br /&gt;
&lt;br /&gt;
;BRCS – Branch if Carry Flag is Set: Der Sprung wird durchgeführt, wenn das Carry-Flag (C) gesetzt ist. Die Verwendung ist sehr ähnlich zu BRCC. BRCS ≡ BRLO&lt;br /&gt;
&lt;br /&gt;
=== Selten verwendete bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
;BRHC – Branch if Half Carry Flag is Cleared: Der Sprung wird durchgeführt, wenn das Half-Carry-Flag (H) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRHS – Branch if Half Carry Flag is Set: Der Sprung wird durchgeführt, wenn das Half-Carry-Flag (H) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRID – Branch if Global Interrupt is Disabled: Der Sprung wird durchgeführt, wenn das Interrupt-Flag (I) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRIE – Branch if Global Interrupt is Enabled: Der Sprung wird durchgeführt, wenn das Interrupt-Flag (I) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRTC – Branch if T Flag is Cleared: Der Sprung wird durchgeführt, wenn das T-Flag nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRTS – Branch if T Flag is Set: Der Sprung wird durchgeführt, wenn das T-Flag gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRVC – Branch if Overflow Cleared: Der Sprung wird durchgeführt, wenn das Overflow-Flag (V) nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
;BRVS – Branch if Overflow Set: Der Sprung wird durchgeführt, wenn das Overflow-Flag (V) gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
==Beispiele==&lt;br /&gt;
&lt;br /&gt;
=== Entscheidungen ===&lt;br /&gt;
&lt;br /&gt;
In jedem Programm kommt früher oder später das Problem, die Ausführung von Codeteilen von irgendwelchen Zahlenwerten, die sich in anderen Registern befinden, abhängig zu machen. Sieht beispielsweise die Aufgabe vor, daß Register r18 auf 0 gesetzt werden soll, falls im Register r17 der Zahlenwert 25 enthalten ist und in allen anderen Fällen soll r18 auf 123 gesetzt werden, dann lautet der Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    cpi     r17, 25         ; vergleiche r17 mit der Konstante 25&lt;br /&gt;
    brne    nicht_gleich    ; wenn nicht gleich, dann mach bei nicht_gleich weiter&lt;br /&gt;
    ldi     r18, 0          ; hier stehen nun Anweisungen für den Fall,&lt;br /&gt;
                            ; dass R17 gleich 25 ist&lt;br /&gt;
    rjmp    weiter          ; meist will man den anderen Zweig nicht durchlaufen, darum der Sprung&lt;br /&gt;
nicht_gleich:&lt;br /&gt;
    ldi     r18, 123        ; hier stehen nun Anweisungen für den Fall,&lt;br /&gt;
                            ; dass R17 ungleich 25 ist&lt;br /&gt;
weiter:                     ; hier geht das Programm weiter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In ähnlicher Weise können die anderen bedingten Sprungbefehle eingesetzt werden, um die üblicherweise vorkommenden Vergleiche auf Gleichheit, Ungleichheit, „größer als“, „kleiner als“ zu realisieren.&lt;br /&gt;
&lt;br /&gt;
===Schleifenkonstrukte===&lt;br /&gt;
&lt;br /&gt;
Ein immer wiederkehrendes Muster in der Programmierung ist eine &#039;&#039;&#039;Schleife&#039;&#039;&#039;. Die einfachste Form einer Schleife ist die &#039;&#039;Zählschleife&#039;&#039;. Dabei wird ein Register von einem Startwert ausgehend eine gewisse Anzahl erhöht, bis ein Endwert erreicht wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    ldi     r17, 10         ; der Startwert sei in diesem Beispiel 10&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    inc     r17             ; erhöhe das Zählregister&lt;br /&gt;
    cpi     r17, 134        ; mit dem Endwert vergleichen&lt;br /&gt;
    brne    loop            ; und wenn der Endwert noch nicht erreicht ist,&lt;br /&gt;
                            ; wird bei der Marke loop ein weiterer Schleifendurchlauf ausgeführt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sehr oft ist es auch möglich, das Konstrukt umzukehren. Anstatt von einem Startwert aus zu inkrementieren genügt es, die Anzahl der gewünschten Schleifendurchläufe in ein Register zu laden und dieses Register zu dekrementieren. Dabei kann man von der Eigenschaft der Dekrementieranweisung Gebrauch machen, das Zero-Flag (Z) zu beeinflussen. Ist das Ergebnis des Dekrements gleich 0, so wird das Zero-Flag gesetzt, welches wiederum in der nachfolgenden BRNE-Anweisung für einen bedingten Sprung benutzt werden kann. Das vereinfacht die Schleife und spart eine Anweisung sowie einen Takt Ausführungszeit.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    ldi     r17, 124        ; Die Anzahl der Wiederholungen in ein Register laden&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    dec     r17             ; Schleifenzähler um 1 verringern, dabei wird das Zero-Flag beeinflusst&lt;br /&gt;
    brne    loop            ; wenn r17 noch nicht 0 geworden ist -&amp;gt; Schleife wiederholen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Literatur==&lt;br /&gt;
* [https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf AVR Instruction Set Manual]: Übersicht aller AVR-Befehle mit Angaben zur Beeinflussung der Statusbits&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Interrupts|&lt;br /&gt;
zurücklink=AVR-Tutorial: Interrupts|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Mehrfachverzweigungen|&lt;br /&gt;
vorlink=AVR-Tutorial: Mehrfachverzweigung}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Vergleiche]]&lt;br /&gt;
[[Category:AVR-Arithmetik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_HV-Programmer&amp;diff=107244</id>
		<title>AVR HV-Programmer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_HV-Programmer&amp;diff=107244"/>
		<updated>2025-01-26T21:28:16Z</updated>

		<summary type="html">&lt;p&gt;Heha: Emuliert (besser) STK600&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein &amp;quot;Hochspannungs&amp;quot;-Programmierer für AVR legt eine Spannung von +12V an den Reset-Eingang. Damit lassen sich auch Fusebits programmieren, die mit dem normalen ISP-Programmer nicht zugänglich sind.&lt;br /&gt;
&lt;br /&gt;
Zu unterscheiden sind das High Voltage Serial Programming ([http://support.atmel.no/knowledgebase/avrstudiohelp/mergedProjects/AVRDragon/AVRDragon_HVSP_Description.htm HVSP]) und das Parallel Programming ([http://support.atmel.no/knowledgebase/avrstudiohelp/mergedProjects/AVRDragon/AVRDragon_PP_Description.htm PP]). Welche Methode(n) ein AVR unterstützt, steht im jeweiligen Datenblatt des AVRs.&lt;br /&gt;
&lt;br /&gt;
Bekannte HV Programmer sind:&lt;br /&gt;
* [[STK500]] (HVSP, PP)&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3891 AVR Dragon] (HVSP, PP)&lt;br /&gt;
* [http://www.der-hammer.info/hvprog/ HVProg von Tobias Hammer] (PP, HVSP)&lt;br /&gt;
* [http://www.b-redemann.de/sp-hvprog1.shtml Bausatz und Anleitung von Bernhard Redemann] (PP)&lt;br /&gt;
* [https://www.obdev.at/products/vusb/avrdoper.html AVR-Doper] High Voltage &#039;&#039;&#039;Serial&#039;&#039;&#039; Programmer (HVSP)&lt;br /&gt;
* [http://mightyohm.com/blog/2008/09/arduino-based-avr-high-voltage-programmer/ Arduino-based AVR High Voltage Programmer] (PP)&lt;br /&gt;
* [http://elm-chan.org/works/avrx/report_e.html &#039;&#039;&#039;Parallel&#039;&#039;&#039; HV-Programmer von ElmChan (ChaN)] (HVSP, PP, TPI)&lt;br /&gt;
* [http://diy.elektroda.eu/atmega-fusebit-doctor-hvpp/?lang=en Atmega fusebit doctor]  (HVSP, PP)&lt;br /&gt;
* [https://guloshop.de/shop/Mikrocontroller-Programmierung/HVSP-Fusebit-Programmer::63.html HVSP Fusebit Programmer] (HVSP, auch stand-alone ohne PC nutzbar)&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/avr-hvsp-fuse-restore.html ATtiny Fuse Restore] Anleitung für einen einfach aufzubauenden ATtiny Fuse Resetter mit AVR-GCC Software von Peter Fleury (HVSP)&lt;br /&gt;
&lt;br /&gt;
Hochvolt-Programmer sind &#039;&#039;nur dann&#039;&#039; als &#039;&#039;&#039;In-System&#039;&#039;&#039;-Programmer verwendbar, wenn die Schaltung mit den 12 V an der Resetleitung zurechtkommt und auch alle anderen Pins genügend hochohmig und ohne schaltungstechnische Nebenwirkungen (etwa einem Brückenkurzschluss) angeschlossen sind. Da dies bei der Menge anzuschließender Pins [im Vergleich zur Gesamtanzahl der Pins des Gehäuses] eher selten der Fall sein wird, werden die Chips fast immer „solo“ (sozusagen „Out-System“) gebrannt. Am ehesten bietet sich HVSP-ISP für 14-beinige SMD-AVRs an; dazu müssen in der Einsatzschaltung ggf. entsprechende Vorkehrungen getroffen werden.&lt;br /&gt;
&lt;br /&gt;
=== Ein Platinenlayout für ElmChans Hochvolt-Programmer ===&lt;br /&gt;
&lt;br /&gt;
Dieser benötigt einen PC mit Parallelport sowie eine externe Stromversorgung von 6..24 V=. USB-Parallel-Konverter (für Drucker) funktionieren &#039;&#039;nicht&#039;&#039;. Vorteil: Ein Mikrocontroller wird nicht benötigt.&lt;br /&gt;
&lt;br /&gt;
Wie auch das Original beherrscht dieser PP (parallel programming) als auch HVSP (serial programming).&lt;br /&gt;
Für den rein seriellen Gebrauch (HVSP der 8- und 14-Beiner) kann man auf den 74HC299 verzichten.&lt;br /&gt;
&lt;br /&gt;
Schaltplan im Eagle-Format, Bestückungspläne und Platinenlayout: [[Benutzer:Christoph kessler|Christoph Kessler]] &lt;br /&gt;
&lt;br /&gt;
ElmChans Konzept ist eine ausgesprochene „Billiglösung“, die 20-poligen AVRs bevorzugend, dem scheint eine einseitige Platine mit Drahtbrücken im Eagle-Light-Format 80×100 mm² angemessen. Jener verzichtet auf Spannungsregler; einen 78L05 hinzuzusetzen und das Ganze mit stabilisierten 12 V zu speisen sollte jedoch nicht allzu schwer fallen.&lt;br /&gt;
&lt;br /&gt;
Das Foto (unten) zeigt schon die „Luxusversion“, Teilbestückung ist entsprechend denkbar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung, Schaltplanfehler!&#039;&#039;&#039; Die Bezeichnungen /AUTOFEED und /SELIN sind genau verkehrt herum.&lt;br /&gt;
&lt;br /&gt;
[[Media:AVR_HV_Programmer_Eagle.zip | Eagle board / schematic files]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schematic.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:Components.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR_HV_Programmer_Layout.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR_HV_Programmer_Foto.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== Software, auch für ATtiny4..10, also TPI ===&lt;br /&gt;
&lt;br /&gt;
Open-Source, für Windows und Linux, jeweils 32 und 64 Bit, winzige Echse-Größe, [http://www.tu-chemnitz.de/~heha/hs/avrpp.zip/ Download].&lt;br /&gt;
Kann direkt ELF-Dateien laden, kein Umweg über &amp;lt;tt&amp;gt;avr-objcopy&amp;lt;/tt&amp;gt; erforderlich.&lt;br /&gt;
Kann EEPROM, Fuses und Lockbits „in einem Rutsch“ der (von-neumannisierten) Hex- oder ELF-Datei&lt;br /&gt;
entnehmen und brennen.&lt;br /&gt;
Sowie die Chip-Signatur aus der Datei (mit &amp;lt;tt&amp;gt;#include &amp;lt;avr/signature.h&amp;gt;&amp;lt;/tt&amp;gt; zu generieren) mit dem Chip vergleichen.&lt;br /&gt;
Bei Verwendung einer ELF-Datei kann die Software die Chip-Signatur von der elf-Section &#039;&#039;&#039;.note.gnu.avr.deviceinfo&#039;&#039;&#039; ermitteln; &amp;lt;tt&amp;gt;#include &amp;lt;avr/signature.h&amp;gt;&amp;lt;/tt&amp;gt; ist dann nicht mehr erforderlich.&lt;br /&gt;
Die Software kennt alle 8-Bit-AVRs.&lt;br /&gt;
Unterstützt direkten (giveio.sys) als auch indirekten (InpOut32.dll) Portzugriff aufs Parallelport sowie eine wahlfreie Port(basis)adresse.&lt;br /&gt;
Läuft von Windows 95 bis Windows 10 sowie Linux; ein PC mit Parallelport (auch als Steckkarte via PCI oder PCIexpress nachgerüstet) ist erforderlich. Parallelports an Docking-Stations funktionieren auch. USB-Parallel-Konverter (für alte Drucker) funktionieren nicht.&lt;br /&gt;
&lt;br /&gt;
Der sechsbeinige Schaltkreis im SOT23-Gehäuse ist wie folgt an die 8-polige Fassung anzuschließen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Pin der Fassung !! Pin des Controllers !! Signalname&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 6 || &amp;lt;span style=&#039;text-decoration:overline&#039;&amp;gt;RESET&amp;lt;/span&amp;gt; = Programmierspannung&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 3 || TPICLK (Takt)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 2 || GND (Bezugspotenzial)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || TPIDATA (Daten, bidirektional)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || 5 || 5P (geschaltet)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Pin 4 des Controllers kann frei bleiben.&lt;br /&gt;
&lt;br /&gt;
==== Fuse bei ATtiny4..10 ====&lt;br /&gt;
&lt;br /&gt;
Diese Mikrocontroller haben laut Dokumentation keine Fuse(s), sondern &#039;&#039;&#039;Configuration Bits&#039;&#039;&#039;.&lt;br /&gt;
avr-gcc trägt diesem Umstand „Rechnung“, dass &amp;lt;tt&amp;gt;#include &amp;amp;lt;avr/fuse.h&amp;amp;gt;&amp;lt;/tt&amp;gt; nicht wie erwartet funktioniert: Die Configuration-Bits sollen laut Linker-Skript in der Sektion &amp;lt;tt&amp;gt;.config&amp;lt;/tt&amp;gt; ankommen, mit der linearen Adresse 0x820000 (die gleiche wie &amp;lt;tt&amp;gt;.fuse&amp;lt;/tt&amp;gt; für die übrigen AVRs).&lt;br /&gt;
In die ELF-Datei setzt man ein einzelnes Configuration-Byte so:&lt;br /&gt;
&lt;br /&gt;
    &#039;&#039;&#039;uint8_t __attribute__((section(&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;&amp;quot;.config&amp;quot;&amp;lt;/span&amp;gt;&#039;&#039;&#039;)))&#039;&#039;&#039; Config=&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;0xFE&amp;lt;/span&amp;gt;;  &#039;&#039;// &amp;lt;span style=&amp;quot;text-decoration:overline&amp;quot;&amp;gt;Reset&amp;lt;/span&amp;gt; = I/O-Pin&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der ELF-Loader vom oben angegebenen &#039;&#039;&#039;avrpp&#039;&#039;&#039; berücksichtigt diese Besonderheit.&lt;br /&gt;
&lt;br /&gt;
==== avr-gcc-Fehler bei ATtiny4 und ATtiny5 ====&lt;br /&gt;
&lt;br /&gt;
Verwendet man&lt;br /&gt;
&lt;br /&gt;
    #include &amp;lt;avr/signature.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verweigert &#039;&#039;&#039;avrpp&#039;&#039;&#039; die Zusammenarbeit mit dem Chip, weil die Signatur nicht passt. Hier liegt der Fehler in den Kopfdateien &amp;lt;tt&amp;gt;avr/tn4.h&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;avr/tn5.h&amp;lt;/tt&amp;gt;, dort sind die Signaturbytes tatsächlich falsch eingetragen.&lt;br /&gt;
Vielleicht liest das jemand vom avr-gcc-Team und packt das Problem an der Wurzel, statt selbst die Dateien korrigieren zu müssen.&lt;br /&gt;
&lt;br /&gt;
=== USB-Hardware ===&lt;br /&gt;
&lt;br /&gt;
Das Verschwinden von Parallelports stellt die größte Hürde für Bastel-Programmiergeräte dar.&lt;br /&gt;
Programmiergeräte mit USB leiden nur dann nicht unter dem Henne-Ei-Problem, wenn der verwendete USB-Mikrocontroller bereits ab Fabrik einen USB-Urlader enthält. Immerhin löst USB das Problem der sonst erforderlichen zusätzlichen Stromversorgung.&lt;br /&gt;
&lt;br /&gt;
Es scheint kein Bastel-USB-Programmiergerät zu geben, welches &#039;&#039;&#039;ISP&#039;&#039;&#039; &#039;&#039;und&#039;&#039; &#039;&#039;&#039;HVSP&#039;&#039;&#039; &#039;&#039;und&#039;&#039; &#039;&#039;&#039;HVPP&#039;&#039;&#039; &#039;&#039;und&#039;&#039; &#039;&#039;&#039;TPI&#039;&#039;&#039; kann! &#039;&#039;&#039;TPI&#039;&#039;&#039; sowohl in Niedervolt- als auch Hochvolt-Ausprägung. Deshalb habe ich hier ein [https://www-user.tu-chemnitz.de/~heha/basteln/pg/avrpp#2 nachbaufähiges Muster] entwickelt. Es basiert auf ATmega16U2, der bereits einen Urlader mitbringt.&lt;br /&gt;
&lt;br /&gt;
Der Gerätezugriff darauf erfolgt noch nicht mit avrpp.exe sondern &amp;lt;tt&amp;gt;avrdude -c stk600&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;stk600pp&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;stk600hvsp&amp;lt;/tt&amp;gt;. Unterstützung für PIC ist ebenfalls vorgesehen, indem die Programmierspannung einstellbar ist. (Das PIC-Programmierinterface ist identisch zu TPI, bloß ein anderer Name und ein anderes Protokoll.)&lt;br /&gt;
&lt;br /&gt;
=== Betrieb mit SMD-Clip ===&lt;br /&gt;
&lt;br /&gt;
Beim Betrieb dieses Programmiergerätes in Verbindung mit einem 8-poligen SMD-Clip zur Programmierung von &#039;&#039;&#039;ATtiny&amp;lt;i&amp;gt;X&amp;lt;/i&amp;gt;5&#039;&#039;&#039; (SU und SSU = breites und normales SMD-Gehäuse mit &#039;&#039;&#039;e&#039;&#039;&#039; = 1,27 mm) versagt bereits das Auslesen der Device-ID-Bytes, und der Chip wird nicht erkannt.&lt;br /&gt;
Auch das Drosseln der Programmiergeschwindigkeit (bspw. mit &#039;&#039;&#039;-i10&#039;&#039;&#039;) hilft nicht dagegen.&lt;br /&gt;
Ursache sind gegenseitige Störungen auf den vier Datenleitungen, die vom Mikrocontroller selbst verursacht werden und über die Leitungslänge von nur 15 cm nicht mehr genügend von den Widerständen auf dem Programmerboard bedämpft werden.&lt;br /&gt;
Dies führt letztlich zu zusätzlichen Taktflanken auf &#039;&#039;&#039;SCI = PB3&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Abhilfe schafft ein &#039;&#039;&#039;Kondensator 100 pF&#039;&#039;&#039; zwischen &#039;&#039;&#039;Pin 2 = SCI = PB3&#039;&#039;&#039; und &#039;&#039;&#039;Pin 4 = GND&#039;&#039;&#039; &#039;&#039;direkt am zu programmierenden Mikrocontroller&#039;&#039;.&lt;br /&gt;
&amp;lt;small&amp;gt;Alternativ ein &#039;&#039;&#039;Kondensator 4,7 nF&#039;&#039;&#039; zwischen &#039;&#039;&#039;Pin 7 = PB2 = SDO&#039;&#039;&#039; und &#039;&#039;&#039;Pin 4 = GND&#039;&#039;&#039;, ebenfalls &#039;&#039;direkt am zu programmierenden Mikrocontroller&#039;&#039;.&lt;br /&gt;
Andere versuchsweise Kondensatoren, insbesondere zwischen &#039;&#039;&#039;Pin 8 = U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&#039;&#039;&#039; und &#039;&#039;&#039;Pin 4 = GND&#039;&#039;&#039; brachten keinen Erfolg.&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe Problem wird vermutlich auch bei anderen 8-poligen ATtinys und beim Verlängern mit 14-poligen &#039;&#039;&#039;ATtiny&amp;lt;i&amp;gt;X&amp;lt;/i&amp;gt;4&#039;&#039;&#039; auftreten, da gleicher Programmieralgorithmus: „HVSP“ = Hochvolt-serielle Programmierung.&lt;br /&gt;
&lt;br /&gt;
Es ist davon auszugehen, dass eine geeignete Terminierung auf der Programmer-Seite (bspw. Serienwiderstände 100 Ω statt 1 kΩ) das Problem ebenfalls und besser löst.&lt;br /&gt;
&lt;br /&gt;
===siehe auch===&lt;br /&gt;
[http://xray37.de/?Bastelbuch:Lima-SDR:RX Miniatur-Version von ElmChans HV-Programmer]&lt;br /&gt;
&lt;br /&gt;
[[AVR In System Programmer]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader]]&lt;br /&gt;
[[Kategorie:AVR-Boards]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_HV-Programmer&amp;diff=107243</id>
		<title>AVR HV-Programmer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_HV-Programmer&amp;diff=107243"/>
		<updated>2025-01-26T21:22:57Z</updated>

		<summary type="html">&lt;p&gt;Heha: ElmChan kann auch TPI für ATtiny4..10&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein &amp;quot;Hochspannungs&amp;quot;-Programmierer für AVR legt eine Spannung von +12V an den Reset-Eingang. Damit lassen sich auch Fusebits programmieren, die mit dem normalen ISP-Programmer nicht zugänglich sind.&lt;br /&gt;
&lt;br /&gt;
Zu unterscheiden sind das High Voltage Serial Programming ([http://support.atmel.no/knowledgebase/avrstudiohelp/mergedProjects/AVRDragon/AVRDragon_HVSP_Description.htm HVSP]) und das Parallel Programming ([http://support.atmel.no/knowledgebase/avrstudiohelp/mergedProjects/AVRDragon/AVRDragon_PP_Description.htm PP]). Welche Methode(n) ein AVR unterstützt, steht im jeweiligen Datenblatt des AVRs.&lt;br /&gt;
&lt;br /&gt;
Bekannte HV Programmer sind:&lt;br /&gt;
* [[STK500]] (HVSP, PP)&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3891 AVR Dragon] (HVSP, PP)&lt;br /&gt;
* [http://www.der-hammer.info/hvprog/ HVProg von Tobias Hammer] (PP, HVSP)&lt;br /&gt;
* [http://www.b-redemann.de/sp-hvprog1.shtml Bausatz und Anleitung von Bernhard Redemann] (PP)&lt;br /&gt;
* [https://www.obdev.at/products/vusb/avrdoper.html AVR-Doper] High Voltage &#039;&#039;&#039;Serial&#039;&#039;&#039; Programmer (HVSP)&lt;br /&gt;
* [http://mightyohm.com/blog/2008/09/arduino-based-avr-high-voltage-programmer/ Arduino-based AVR High Voltage Programmer] (PP)&lt;br /&gt;
* [http://elm-chan.org/works/avrx/report_e.html &#039;&#039;&#039;Parallel&#039;&#039;&#039; HV-Programmer von ElmChan (ChaN)] (HVSP, PP, TPI)&lt;br /&gt;
* [http://diy.elektroda.eu/atmega-fusebit-doctor-hvpp/?lang=en Atmega fusebit doctor]  (HVSP, PP)&lt;br /&gt;
* [https://guloshop.de/shop/Mikrocontroller-Programmierung/HVSP-Fusebit-Programmer::63.html HVSP Fusebit Programmer] (HVSP, auch stand-alone ohne PC nutzbar)&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/avr-hvsp-fuse-restore.html ATtiny Fuse Restore] Anleitung für einen einfach aufzubauenden ATtiny Fuse Resetter mit AVR-GCC Software von Peter Fleury (HVSP)&lt;br /&gt;
&lt;br /&gt;
Hochvolt-Programmer sind &#039;&#039;nur dann&#039;&#039; als &#039;&#039;&#039;In-System&#039;&#039;&#039;-Programmer verwendbar, wenn die Schaltung mit den 12 V an der Resetleitung zurechtkommt und auch alle anderen Pins genügend hochohmig und ohne schaltungstechnische Nebenwirkungen (etwa einem Brückenkurzschluss) angeschlossen sind. Da dies bei der Menge anzuschließender Pins [im Vergleich zur Gesamtanzahl der Pins des Gehäuses] eher selten der Fall sein wird, werden die Chips fast immer „solo“ (sozusagen „Out-System“) gebrannt. Am ehesten bietet sich HVSP-ISP für 14-beinige SMD-AVRs an; dazu müssen in der Einsatzschaltung ggf. entsprechende Vorkehrungen getroffen werden.&lt;br /&gt;
&lt;br /&gt;
=== Ein Platinenlayout für ElmChans Hochvolt-Programmer ===&lt;br /&gt;
&lt;br /&gt;
Dieser benötigt einen PC mit Parallelport sowie eine externe Stromversorgung von 6..24 V=. USB-Parallel-Konverter (für Drucker) funktionieren &#039;&#039;nicht&#039;&#039;. Vorteil: Ein Mikrocontroller wird nicht benötigt.&lt;br /&gt;
&lt;br /&gt;
Wie auch das Original beherrscht dieser PP (parallel programming) als auch HVSP (serial programming).&lt;br /&gt;
Für den rein seriellen Gebrauch (HVSP der 8- und 14-Beiner) kann man auf den 74HC299 verzichten.&lt;br /&gt;
&lt;br /&gt;
Schaltplan im Eagle-Format, Bestückungspläne und Platinenlayout: [[Benutzer:Christoph kessler|Christoph Kessler]] &lt;br /&gt;
&lt;br /&gt;
ElmChans Konzept ist eine ausgesprochene „Billiglösung“, die 20-poligen AVRs bevorzugend, dem scheint eine einseitige Platine mit Drahtbrücken im Eagle-Light-Format 80×100 mm² angemessen. Jener verzichtet auf Spannungsregler; einen 78L05 hinzuzusetzen und das Ganze mit stabilisierten 12 V zu speisen sollte jedoch nicht allzu schwer fallen.&lt;br /&gt;
&lt;br /&gt;
Das Foto (unten) zeigt schon die „Luxusversion“, Teilbestückung ist entsprechend denkbar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung, Schaltplanfehler!&#039;&#039;&#039; Die Bezeichnungen /AUTOFEED und /SELIN sind genau verkehrt herum.&lt;br /&gt;
&lt;br /&gt;
[[Media:AVR_HV_Programmer_Eagle.zip | Eagle board / schematic files]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schematic.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:Components.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR_HV_Programmer_Layout.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR_HV_Programmer_Foto.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== Software, auch für ATtiny4..10, also TPI ===&lt;br /&gt;
&lt;br /&gt;
Open-Source, für Windows und Linux, jeweils 32 und 64 Bit, winzige Echse-Größe, [http://www.tu-chemnitz.de/~heha/hs/avrpp.zip/ Download].&lt;br /&gt;
Kann direkt ELF-Dateien laden, kein Umweg über &amp;lt;tt&amp;gt;avr-objcopy&amp;lt;/tt&amp;gt; erforderlich.&lt;br /&gt;
Kann EEPROM, Fuses und Lockbits „in einem Rutsch“ der (von-neumannisierten) Hex- oder ELF-Datei&lt;br /&gt;
entnehmen und brennen.&lt;br /&gt;
Sowie die Chip-Signatur aus der Datei (mit &amp;lt;tt&amp;gt;#include &amp;lt;avr/signature.h&amp;gt;&amp;lt;/tt&amp;gt; zu generieren) mit dem Chip vergleichen.&lt;br /&gt;
Bei Verwendung einer ELF-Datei kann die Software die Chip-Signatur von der elf-Section &#039;&#039;&#039;.note.gnu.avr.deviceinfo&#039;&#039;&#039; ermitteln; &amp;lt;tt&amp;gt;#include &amp;lt;avr/signature.h&amp;gt;&amp;lt;/tt&amp;gt; ist dann nicht mehr erforderlich.&lt;br /&gt;
Die Software kennt alle 8-Bit-AVRs.&lt;br /&gt;
Unterstützt direkten (giveio.sys) als auch indirekten (InpOut32.dll) Portzugriff aufs Parallelport sowie eine wahlfreie Port(basis)adresse.&lt;br /&gt;
Läuft von Windows 95 bis Windows 10 sowie Linux; ein PC mit Parallelport (auch als Steckkarte via PCI oder PCIexpress nachgerüstet) ist erforderlich. Parallelports an Docking-Stations funktionieren auch. USB-Parallel-Konverter (für alte Drucker) funktionieren nicht.&lt;br /&gt;
&lt;br /&gt;
Der sechsbeinige Schaltkreis im SOT23-Gehäuse ist wie folgt an die 8-polige Fassung anzuschließen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Pin der Fassung !! Pin des Controllers !! Signalname&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 6 || &amp;lt;span style=&#039;text-decoration:overline&#039;&amp;gt;RESET&amp;lt;/span&amp;gt; = Programmierspannung&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 3 || TPICLK (Takt)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 2 || GND (Bezugspotenzial)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || TPIDATA (Daten, bidirektional)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || 5 || 5P (geschaltet)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Pin 4 des Controllers kann frei bleiben.&lt;br /&gt;
&lt;br /&gt;
==== Fuse bei ATtiny4..10 ====&lt;br /&gt;
&lt;br /&gt;
Diese Mikrocontroller haben laut Dokumentation keine Fuse(s), sondern &#039;&#039;&#039;Configuration Bits&#039;&#039;&#039;.&lt;br /&gt;
avr-gcc trägt diesem Umstand „Rechnung“, dass &amp;lt;tt&amp;gt;#include &amp;amp;lt;avr/fuse.h&amp;amp;gt;&amp;lt;/tt&amp;gt; nicht wie erwartet funktioniert: Die Configuration-Bits sollen laut Linker-Skript in der Sektion &amp;lt;tt&amp;gt;.config&amp;lt;/tt&amp;gt; ankommen, mit der linearen Adresse 0x820000 (die gleiche wie &amp;lt;tt&amp;gt;.fuse&amp;lt;/tt&amp;gt; für die übrigen AVRs).&lt;br /&gt;
In die ELF-Datei setzt man ein einzelnes Configuration-Byte so:&lt;br /&gt;
&lt;br /&gt;
    &#039;&#039;&#039;uint8_t __attribute__((section(&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;&amp;quot;.config&amp;quot;&amp;lt;/span&amp;gt;&#039;&#039;&#039;)))&#039;&#039;&#039; Config=&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;0xFE&amp;lt;/span&amp;gt;;  &#039;&#039;// &amp;lt;span style=&amp;quot;text-decoration:overline&amp;quot;&amp;gt;Reset&amp;lt;/span&amp;gt; = I/O-Pin&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der ELF-Loader vom oben angegebenen &#039;&#039;&#039;avrpp&#039;&#039;&#039; berücksichtigt diese Besonderheit.&lt;br /&gt;
&lt;br /&gt;
==== avr-gcc-Fehler bei ATtiny4 und ATtiny5 ====&lt;br /&gt;
&lt;br /&gt;
Verwendet man&lt;br /&gt;
&lt;br /&gt;
    #include &amp;lt;avr/signature.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verweigert &#039;&#039;&#039;avrpp&#039;&#039;&#039; die Zusammenarbeit mit dem Chip, weil die Signatur nicht passt. Hier liegt der Fehler in den Kopfdateien &amp;lt;tt&amp;gt;avr/tn4.h&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;avr/tn5.h&amp;lt;/tt&amp;gt;, dort sind die Signaturbytes tatsächlich falsch eingetragen.&lt;br /&gt;
Vielleicht liest das jemand vom avr-gcc-Team und packt das Problem an der Wurzel, statt selbst die Dateien korrigieren zu müssen.&lt;br /&gt;
&lt;br /&gt;
=== USB-Hardware ===&lt;br /&gt;
&lt;br /&gt;
Das Verschwinden von Parallelports stellt die größte Hürde für Bastel-Programmiergeräte dar.&lt;br /&gt;
Programmiergeräte mit USB leiden nur dann nicht unter dem Henne-Ei-Problem, wenn der verwendete USB-Mikrocontroller bereits ab Fabrik einen USB-Urlader enthält. Immerhin löst USB das Problem der sonst erforderlichen zusätzlichen Stromversorgung.&lt;br /&gt;
&lt;br /&gt;
Es scheint kein Bastel-USB-Programmiergerät zu geben, welches &#039;&#039;&#039;ISP&#039;&#039;&#039; &#039;&#039;und&#039;&#039; &#039;&#039;&#039;HVSP&#039;&#039;&#039; &#039;&#039;und&#039;&#039; &#039;&#039;&#039;HVPP&#039;&#039;&#039; &#039;&#039;und&#039;&#039; &#039;&#039;&#039;TPI&#039;&#039;&#039; kann! &#039;&#039;&#039;TPI&#039;&#039;&#039; sowohl in Niedervolt- als auch Hochvolt-Ausprägung. Deshalb habe ich hier ein [https://www-user.tu-chemnitz.de/~heha/basteln/pg/avrpp#2 nachbaufähiges Muster] entwickelt. Es basiert auf ATmega16U2, der bereits einen Urlader mitbringt.&lt;br /&gt;
&lt;br /&gt;
Derzeit funktioniert TPI noch nicht. Der Gerätezugriff darauf erfolgt noch nicht mit avrpp.exe sondern &amp;lt;tt&amp;gt;avrdude -c stk500&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;stk500pp&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;stk500hvsp&amp;lt;/tt&amp;gt;. Unterstützung für PIC ist ebenfalls vorgesehen, indem die Programmierspannung einstellbar ist. (Das PIC-Programmierinterface ist identisch zu TPI, bloß ein anderer Name und ein anderes Protokoll.)&lt;br /&gt;
&lt;br /&gt;
=== Betrieb mit SMD-Clip ===&lt;br /&gt;
&lt;br /&gt;
Beim Betrieb dieses Programmiergerätes in Verbindung mit einem 8-poligen SMD-Clip zur Programmierung von &#039;&#039;&#039;ATtiny&amp;lt;i&amp;gt;X&amp;lt;/i&amp;gt;5&#039;&#039;&#039; (SU und SSU = breites und normales SMD-Gehäuse mit &#039;&#039;&#039;e&#039;&#039;&#039; = 1,27 mm) versagt bereits das Auslesen der Device-ID-Bytes, und der Chip wird nicht erkannt.&lt;br /&gt;
Auch das Drosseln der Programmiergeschwindigkeit (bspw. mit &#039;&#039;&#039;-i10&#039;&#039;&#039;) hilft nicht dagegen.&lt;br /&gt;
Ursache sind gegenseitige Störungen auf den vier Datenleitungen, die vom Mikrocontroller selbst verursacht werden und über die Leitungslänge von nur 15 cm nicht mehr genügend von den Widerständen auf dem Programmerboard bedämpft werden.&lt;br /&gt;
Dies führt letztlich zu zusätzlichen Taktflanken auf &#039;&#039;&#039;SCI = PB3&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Abhilfe schafft ein &#039;&#039;&#039;Kondensator 100 pF&#039;&#039;&#039; zwischen &#039;&#039;&#039;Pin 2 = SCI = PB3&#039;&#039;&#039; und &#039;&#039;&#039;Pin 4 = GND&#039;&#039;&#039; &#039;&#039;direkt am zu programmierenden Mikrocontroller&#039;&#039;.&lt;br /&gt;
&amp;lt;small&amp;gt;Alternativ ein &#039;&#039;&#039;Kondensator 4,7 nF&#039;&#039;&#039; zwischen &#039;&#039;&#039;Pin 7 = PB2 = SDO&#039;&#039;&#039; und &#039;&#039;&#039;Pin 4 = GND&#039;&#039;&#039;, ebenfalls &#039;&#039;direkt am zu programmierenden Mikrocontroller&#039;&#039;.&lt;br /&gt;
Andere versuchsweise Kondensatoren, insbesondere zwischen &#039;&#039;&#039;Pin 8 = U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&#039;&#039;&#039; und &#039;&#039;&#039;Pin 4 = GND&#039;&#039;&#039; brachten keinen Erfolg.&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe Problem wird vermutlich auch bei anderen 8-poligen ATtinys und beim Verlängern mit 14-poligen &#039;&#039;&#039;ATtiny&amp;lt;i&amp;gt;X&amp;lt;/i&amp;gt;4&#039;&#039;&#039; auftreten, da gleicher Programmieralgorithmus: „HVSP“ = Hochvolt-serielle Programmierung.&lt;br /&gt;
&lt;br /&gt;
Es ist davon auszugehen, dass eine geeignete Terminierung auf der Programmer-Seite (bspw. Serienwiderstände 100 Ω statt 1 kΩ) das Problem ebenfalls und besser löst.&lt;br /&gt;
&lt;br /&gt;
===siehe auch===&lt;br /&gt;
[http://xray37.de/?Bastelbuch:Lima-SDR:RX Miniatur-Version von ElmChans HV-Programmer]&lt;br /&gt;
&lt;br /&gt;
[[AVR In System Programmer]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader]]&lt;br /&gt;
[[Kategorie:AVR-Boards]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_HV-Programmer&amp;diff=107236</id>
		<title>AVR HV-Programmer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_HV-Programmer&amp;diff=107236"/>
		<updated>2025-01-23T09:38:42Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* Software, auch für ATtiny4..10, also TPI */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein &amp;quot;Hochspannungs&amp;quot;-Programmierer für AVR legt eine Spannung von +12V an den Reset-Eingang. Damit lassen sich auch Fusebits programmieren, die mit dem normalen ISP-Programmer nicht zugänglich sind.&lt;br /&gt;
&lt;br /&gt;
Zu unterscheiden sind das High Voltage Serial Programming ([http://support.atmel.no/knowledgebase/avrstudiohelp/mergedProjects/AVRDragon/AVRDragon_HVSP_Description.htm HVSP]) und das Parallel Programming ([http://support.atmel.no/knowledgebase/avrstudiohelp/mergedProjects/AVRDragon/AVRDragon_PP_Description.htm PP]). Welche Methode(n) ein AVR unterstützt, steht im jeweiligen Datenblatt des AVRs.&lt;br /&gt;
&lt;br /&gt;
Bekannte HV Programmer sind:&lt;br /&gt;
* [[STK500]] (HVSP, PP)&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3891 AVR Dragon] (HVSP, PP)&lt;br /&gt;
* [http://www.der-hammer.info/hvprog/ HVProg von Tobias Hammer] (PP, HVSP)&lt;br /&gt;
* [http://www.b-redemann.de/sp-hvprog1.shtml Bausatz und Anleitung von Bernhard Redemann] (PP)&lt;br /&gt;
* [https://www.obdev.at/products/vusb/avrdoper.html AVR-Doper] High Voltage &#039;&#039;&#039;Serial&#039;&#039;&#039; Programmer (HVSP)&lt;br /&gt;
* [http://mightyohm.com/blog/2008/09/arduino-based-avr-high-voltage-programmer/ Arduino-based AVR High Voltage Programmer] (PP)&lt;br /&gt;
* [http://elm-chan.org/works/avrx/report_e.html &#039;&#039;&#039;Parallel&#039;&#039;&#039; HV-Programmer von ElmChan (ChaN)] (HVSP, PP)&lt;br /&gt;
* [http://diy.elektroda.eu/atmega-fusebit-doctor-hvpp/?lang=en Atmega fusebit doctor]  (HVSP, PP)&lt;br /&gt;
* [https://guloshop.de/shop/Mikrocontroller-Programmierung/HVSP-Fusebit-Programmer::63.html HVSP Fusebit Programmer] (HVSP, auch stand-alone ohne PC nutzbar)&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/avr-hvsp-fuse-restore.html ATtiny Fuse Restore] Anleitung für einen einfach aufzubauenden ATtiny Fuse Resetter mit AVR-GCC Software von Peter Fleury (HVSP)&lt;br /&gt;
&lt;br /&gt;
Hochvolt-Programmer sind &#039;&#039;nur dann&#039;&#039; als &#039;&#039;&#039;In-System&#039;&#039;&#039;-Programmer verwendbar, wenn die Schaltung mit den 12 V an der Resetleitung zurechtkommt und auch alle anderen Pins genügend hochohmig und ohne schaltungstechnische Nebenwirkungen (etwa einem Brückenkurzschluss) angeschlossen sind. Da dies bei der Menge anzuschließender Pins [im Vergleich zur Gesamtanzahl der Pins des Gehäuses] eher selten der Fall sein wird, werden die Chips fast immer „solo“ (sozusagen „Out-System“) gebrannt. Am ehesten bietet sich HVSP-ISP für 14-beinige SMD-AVRs an; dazu müssen in der Einsatzschaltung ggf. entsprechende Vorkehrungen getroffen werden.&lt;br /&gt;
&lt;br /&gt;
=== Ein Platinenlayout für ElmChans Hochvolt-Programmer ===&lt;br /&gt;
&lt;br /&gt;
Dieser benötigt einen PC mit Parallelport sowie eine externe Stromversorgung von 6..24 V=. USB-Parallel-Konverter (für Drucker) funktionieren &#039;&#039;nicht&#039;&#039;. Vorteil: Ein Mikrocontroller wird nicht benötigt.&lt;br /&gt;
&lt;br /&gt;
Wie auch das Original beherrscht dieser PP (parallel programming) als auch HVSP (serial programming).&lt;br /&gt;
Für den rein seriellen Gebrauch (HVSP der 8- und 14-Beiner) kann man auf den 74HC299 verzichten.&lt;br /&gt;
&lt;br /&gt;
Schaltplan im Eagle-Format, Bestückungspläne und Platinenlayout: [[Benutzer:Christoph kessler|Christoph Kessler]] &lt;br /&gt;
&lt;br /&gt;
ElmChans Konzept ist eine ausgesprochene „Billiglösung“, die 20-poligen AVRs bevorzugend, dem scheint eine einseitige Platine mit Drahtbrücken im Eagle-Light-Format 80×100 mm² angemessen. Jener verzichtet auf Spannungsregler; einen 78L05 hinzuzusetzen und das Ganze mit stabilisierten 12 V zu speisen sollte jedoch nicht allzu schwer fallen.&lt;br /&gt;
&lt;br /&gt;
Das Foto (unten) zeigt schon die „Luxusversion“, Teilbestückung ist entsprechend denkbar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung, Schaltplanfehler!&#039;&#039;&#039; Die Bezeichnungen /AUTOFEED und /SELIN sind genau verkehrt herum.&lt;br /&gt;
&lt;br /&gt;
[[Media:AVR_HV_Programmer_Eagle.zip | Eagle board / schematic files]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schematic.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:Components.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR_HV_Programmer_Layout.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR_HV_Programmer_Foto.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== Software, auch für ATtiny4..10, also TPI ===&lt;br /&gt;
&lt;br /&gt;
Open-Source, für Windows und Linux, jeweils 32 und 64 Bit, winzige Echse-Größe, [http://www.tu-chemnitz.de/~heha/hs/avrpp.zip/ Download].&lt;br /&gt;
Kann direkt ELF-Dateien laden, kein Umweg über &amp;lt;tt&amp;gt;avr-objcopy&amp;lt;/tt&amp;gt; erforderlich.&lt;br /&gt;
Kann EEPROM, Fuses und Lockbits „in einem Rutsch“ der (von-neumannisierten) Hex- oder ELF-Datei&lt;br /&gt;
entnehmen und brennen.&lt;br /&gt;
Sowie die Chip-Signatur aus der Datei (mit &amp;lt;tt&amp;gt;#include &amp;lt;avr/signature.h&amp;gt;&amp;lt;/tt&amp;gt; zu generieren) mit dem Chip vergleichen.&lt;br /&gt;
Bei Verwendung einer ELF-Datei kann die Software die Chip-Signatur von der elf-Section &#039;&#039;&#039;.note.gnu.avr.deviceinfo&#039;&#039;&#039; ermitteln; &amp;lt;tt&amp;gt;#include &amp;lt;avr/signature.h&amp;gt;&amp;lt;/tt&amp;gt; ist dann nicht mehr erforderlich.&lt;br /&gt;
Die Software kennt alle 8-Bit-AVRs.&lt;br /&gt;
Unterstützt direkten (giveio.sys) als auch indirekten (InpOut32.dll) Portzugriff aufs Parallelport sowie eine wahlfreie Port(basis)adresse.&lt;br /&gt;
Läuft von Windows 95 bis Windows 10 sowie Linux; ein PC mit Parallelport (auch als Steckkarte via PCI oder PCIexpress nachgerüstet) ist erforderlich. Parallelports an Docking-Stations funktionieren auch. USB-Parallel-Konverter (für alte Drucker) funktionieren nicht.&lt;br /&gt;
&lt;br /&gt;
Der sechsbeinige Schaltkreis im SOT23-Gehäuse ist wie folgt an die 8-polige Fassung anzuschließen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Pin der Fassung !! Pin des Controllers !! Signalname&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 6 || &amp;lt;span style=&#039;text-decoration:overline&#039;&amp;gt;RESET&amp;lt;/span&amp;gt; = Programmierspannung&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 3 || TPICLK (Takt)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 2 || GND (Bezugspotenzial)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || TPIDATA (Daten, bidirektional)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || 5 || 5P (geschaltet)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Pin 4 des Controllers kann frei bleiben.&lt;br /&gt;
&lt;br /&gt;
==== Fuse bei ATtiny4..10 ====&lt;br /&gt;
&lt;br /&gt;
Diese Mikrocontroller haben laut Dokumentation keine Fuse(s), sondern &#039;&#039;&#039;Configuration Bits&#039;&#039;&#039;.&lt;br /&gt;
avr-gcc trägt diesem Umstand „Rechnung“, dass &amp;lt;tt&amp;gt;#include &amp;amp;lt;avr/fuse.h&amp;amp;gt;&amp;lt;/tt&amp;gt; nicht wie erwartet funktioniert: Die Configuration-Bits sollen laut Linker-Skript in der Sektion &amp;lt;tt&amp;gt;.config&amp;lt;/tt&amp;gt; ankommen, mit der linearen Adresse 0x820000 (die gleiche wie &amp;lt;tt&amp;gt;.fuse&amp;lt;/tt&amp;gt; für die übrigen AVRs).&lt;br /&gt;
In die ELF-Datei setzt man ein einzelnes Configuration-Byte so:&lt;br /&gt;
&lt;br /&gt;
    &#039;&#039;&#039;uint8_t __attribute__((section(&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;&amp;quot;.config&amp;quot;&amp;lt;/span&amp;gt;&#039;&#039;&#039;)))&#039;&#039;&#039; Config=&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;0xFE&amp;lt;/span&amp;gt;;  &#039;&#039;// &amp;lt;span style=&amp;quot;text-decoration:overline&amp;quot;&amp;gt;Reset&amp;lt;/span&amp;gt; = I/O-Pin&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der ELF-Loader vom oben angegebenen &#039;&#039;&#039;avrpp&#039;&#039;&#039; berücksichtigt diese Besonderheit.&lt;br /&gt;
&lt;br /&gt;
==== avr-gcc-Fehler bei ATtiny4 und ATtiny5 ====&lt;br /&gt;
&lt;br /&gt;
Verwendet man&lt;br /&gt;
&lt;br /&gt;
    #include &amp;lt;avr/signature.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verweigert &#039;&#039;&#039;avrpp&#039;&#039;&#039; die Zusammenarbeit mit dem Chip, weil die Signatur nicht passt. Hier liegt der Fehler in den Kopfdateien &amp;lt;tt&amp;gt;avr/tn4.h&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;avr/tn5.h&amp;lt;/tt&amp;gt;, dort sind die Signaturbytes tatsächlich falsch eingetragen.&lt;br /&gt;
Vielleicht liest das jemand vom avr-gcc-Team und packt das Problem an der Wurzel, statt selbst die Dateien korrigieren zu müssen.&lt;br /&gt;
&lt;br /&gt;
=== USB-Hardware ===&lt;br /&gt;
&lt;br /&gt;
Das Verschwinden von Parallelports stellt die größte Hürde für Bastel-Programmiergeräte dar.&lt;br /&gt;
Programmiergeräte mit USB leiden nur dann nicht unter dem Henne-Ei-Problem, wenn der verwendete USB-Mikrocontroller bereits ab Fabrik einen USB-Urlader enthält. Immerhin löst USB das Problem der sonst erforderlichen zusätzlichen Stromversorgung.&lt;br /&gt;
&lt;br /&gt;
Es scheint kein Bastel-USB-Programmiergerät zu geben, welches &#039;&#039;&#039;ISP&#039;&#039;&#039; &#039;&#039;und&#039;&#039; &#039;&#039;&#039;HVSP&#039;&#039;&#039; &#039;&#039;und&#039;&#039; &#039;&#039;&#039;HVPP&#039;&#039;&#039; &#039;&#039;und&#039;&#039; &#039;&#039;&#039;TPI&#039;&#039;&#039; kann! &#039;&#039;&#039;TPI&#039;&#039;&#039; sowohl in Niedervolt- als auch Hochvolt-Ausprägung. Deshalb habe ich hier ein [https://www-user.tu-chemnitz.de/~heha/basteln/pg/avrpp#2 nachbaufähiges Muster] entwickelt. Es basiert auf ATmega16U2, der bereits einen Urlader mitbringt.&lt;br /&gt;
&lt;br /&gt;
Derzeit funktioniert TPI noch nicht. Der Gerätezugriff darauf erfolgt noch nicht mit avrpp.exe sondern &amp;lt;tt&amp;gt;avrdude -c stk500&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;stk500pp&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;stk500hvsp&amp;lt;/tt&amp;gt;. Unterstützung für PIC ist ebenfalls vorgesehen, indem die Programmierspannung einstellbar ist. (Das PIC-Programmierinterface ist identisch zu TPI, bloß ein anderer Name und ein anderes Protokoll.)&lt;br /&gt;
&lt;br /&gt;
=== Betrieb mit SMD-Clip ===&lt;br /&gt;
&lt;br /&gt;
Beim Betrieb dieses Programmiergerätes in Verbindung mit einem 8-poligen SMD-Clip zur Programmierung von &#039;&#039;&#039;ATtiny&amp;lt;i&amp;gt;X&amp;lt;/i&amp;gt;5&#039;&#039;&#039; (SU und SSU = breites und normales SMD-Gehäuse mit &#039;&#039;&#039;e&#039;&#039;&#039; = 1,27 mm) versagt bereits das Auslesen der Device-ID-Bytes, und der Chip wird nicht erkannt.&lt;br /&gt;
Auch das Drosseln der Programmiergeschwindigkeit (bspw. mit &#039;&#039;&#039;-i10&#039;&#039;&#039;) hilft nicht dagegen.&lt;br /&gt;
Ursache sind gegenseitige Störungen auf den vier Datenleitungen, die vom Mikrocontroller selbst verursacht werden und über die Leitungslänge von nur 15 cm nicht mehr genügend von den Widerständen auf dem Programmerboard bedämpft werden.&lt;br /&gt;
Dies führt letztlich zu zusätzlichen Taktflanken auf &#039;&#039;&#039;SCI = PB3&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Abhilfe schafft ein &#039;&#039;&#039;Kondensator 100 pF&#039;&#039;&#039; zwischen &#039;&#039;&#039;Pin 2 = SCI = PB3&#039;&#039;&#039; und &#039;&#039;&#039;Pin 4 = GND&#039;&#039;&#039; &#039;&#039;direkt am zu programmierenden Mikrocontroller&#039;&#039;.&lt;br /&gt;
&amp;lt;small&amp;gt;Alternativ ein &#039;&#039;&#039;Kondensator 4,7 nF&#039;&#039;&#039; zwischen &#039;&#039;&#039;Pin 7 = PB2 = SDO&#039;&#039;&#039; und &#039;&#039;&#039;Pin 4 = GND&#039;&#039;&#039;, ebenfalls &#039;&#039;direkt am zu programmierenden Mikrocontroller&#039;&#039;.&lt;br /&gt;
Andere versuchsweise Kondensatoren, insbesondere zwischen &#039;&#039;&#039;Pin 8 = U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&#039;&#039;&#039; und &#039;&#039;&#039;Pin 4 = GND&#039;&#039;&#039; brachten keinen Erfolg.&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe Problem wird vermutlich auch bei anderen 8-poligen ATtinys und beim Verlängern mit 14-poligen &#039;&#039;&#039;ATtiny&amp;lt;i&amp;gt;X&amp;lt;/i&amp;gt;4&#039;&#039;&#039; auftreten, da gleicher Programmieralgorithmus: „HVSP“ = Hochvolt-serielle Programmierung.&lt;br /&gt;
&lt;br /&gt;
Es ist davon auszugehen, dass eine geeignete Terminierung auf der Programmer-Seite (bspw. Serienwiderstände 100 Ω statt 1 kΩ) das Problem ebenfalls und besser löst.&lt;br /&gt;
&lt;br /&gt;
===siehe auch===&lt;br /&gt;
[http://xray37.de/?Bastelbuch:Lima-SDR:RX Miniatur-Version von ElmChans HV-Programmer]&lt;br /&gt;
&lt;br /&gt;
[[AVR In System Programmer]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader]]&lt;br /&gt;
[[Kategorie:AVR-Boards]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_HV-Programmer&amp;diff=107235</id>
		<title>AVR HV-Programmer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_HV-Programmer&amp;diff=107235"/>
		<updated>2025-01-23T09:10:40Z</updated>

		<summary type="html">&lt;p&gt;Heha: USB-Hardware&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein &amp;quot;Hochspannungs&amp;quot;-Programmierer für AVR legt eine Spannung von +12V an den Reset-Eingang. Damit lassen sich auch Fusebits programmieren, die mit dem normalen ISP-Programmer nicht zugänglich sind.&lt;br /&gt;
&lt;br /&gt;
Zu unterscheiden sind das High Voltage Serial Programming ([http://support.atmel.no/knowledgebase/avrstudiohelp/mergedProjects/AVRDragon/AVRDragon_HVSP_Description.htm HVSP]) und das Parallel Programming ([http://support.atmel.no/knowledgebase/avrstudiohelp/mergedProjects/AVRDragon/AVRDragon_PP_Description.htm PP]). Welche Methode(n) ein AVR unterstützt, steht im jeweiligen Datenblatt des AVRs.&lt;br /&gt;
&lt;br /&gt;
Bekannte HV Programmer sind:&lt;br /&gt;
* [[STK500]] (HVSP, PP)&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3891 AVR Dragon] (HVSP, PP)&lt;br /&gt;
* [http://www.der-hammer.info/hvprog/ HVProg von Tobias Hammer] (PP, HVSP)&lt;br /&gt;
* [http://www.b-redemann.de/sp-hvprog1.shtml Bausatz und Anleitung von Bernhard Redemann] (PP)&lt;br /&gt;
* [https://www.obdev.at/products/vusb/avrdoper.html AVR-Doper] High Voltage &#039;&#039;&#039;Serial&#039;&#039;&#039; Programmer (HVSP)&lt;br /&gt;
* [http://mightyohm.com/blog/2008/09/arduino-based-avr-high-voltage-programmer/ Arduino-based AVR High Voltage Programmer] (PP)&lt;br /&gt;
* [http://elm-chan.org/works/avrx/report_e.html &#039;&#039;&#039;Parallel&#039;&#039;&#039; HV-Programmer von ElmChan (ChaN)] (HVSP, PP)&lt;br /&gt;
* [http://diy.elektroda.eu/atmega-fusebit-doctor-hvpp/?lang=en Atmega fusebit doctor]  (HVSP, PP)&lt;br /&gt;
* [https://guloshop.de/shop/Mikrocontroller-Programmierung/HVSP-Fusebit-Programmer::63.html HVSP Fusebit Programmer] (HVSP, auch stand-alone ohne PC nutzbar)&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/avr-hvsp-fuse-restore.html ATtiny Fuse Restore] Anleitung für einen einfach aufzubauenden ATtiny Fuse Resetter mit AVR-GCC Software von Peter Fleury (HVSP)&lt;br /&gt;
&lt;br /&gt;
Hochvolt-Programmer sind &#039;&#039;nur dann&#039;&#039; als &#039;&#039;&#039;In-System&#039;&#039;&#039;-Programmer verwendbar, wenn die Schaltung mit den 12 V an der Resetleitung zurechtkommt und auch alle anderen Pins genügend hochohmig und ohne schaltungstechnische Nebenwirkungen (etwa einem Brückenkurzschluss) angeschlossen sind. Da dies bei der Menge anzuschließender Pins [im Vergleich zur Gesamtanzahl der Pins des Gehäuses] eher selten der Fall sein wird, werden die Chips fast immer „solo“ (sozusagen „Out-System“) gebrannt. Am ehesten bietet sich HVSP-ISP für 14-beinige SMD-AVRs an; dazu müssen in der Einsatzschaltung ggf. entsprechende Vorkehrungen getroffen werden.&lt;br /&gt;
&lt;br /&gt;
=== Ein Platinenlayout für ElmChans Hochvolt-Programmer ===&lt;br /&gt;
&lt;br /&gt;
Dieser benötigt einen PC mit Parallelport sowie eine externe Stromversorgung von 6..24 V=. USB-Parallel-Konverter (für Drucker) funktionieren &#039;&#039;nicht&#039;&#039;. Vorteil: Ein Mikrocontroller wird nicht benötigt.&lt;br /&gt;
&lt;br /&gt;
Wie auch das Original beherrscht dieser PP (parallel programming) als auch HVSP (serial programming).&lt;br /&gt;
Für den rein seriellen Gebrauch (HVSP der 8- und 14-Beiner) kann man auf den 74HC299 verzichten.&lt;br /&gt;
&lt;br /&gt;
Schaltplan im Eagle-Format, Bestückungspläne und Platinenlayout: [[Benutzer:Christoph kessler|Christoph Kessler]] &lt;br /&gt;
&lt;br /&gt;
ElmChans Konzept ist eine ausgesprochene „Billiglösung“, die 20-poligen AVRs bevorzugend, dem scheint eine einseitige Platine mit Drahtbrücken im Eagle-Light-Format 80×100 mm² angemessen. Jener verzichtet auf Spannungsregler; einen 78L05 hinzuzusetzen und das Ganze mit stabilisierten 12 V zu speisen sollte jedoch nicht allzu schwer fallen.&lt;br /&gt;
&lt;br /&gt;
Das Foto (unten) zeigt schon die „Luxusversion“, Teilbestückung ist entsprechend denkbar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung, Schaltplanfehler!&#039;&#039;&#039; Die Bezeichnungen /AUTOFEED und /SELIN sind genau verkehrt herum.&lt;br /&gt;
&lt;br /&gt;
[[Media:AVR_HV_Programmer_Eagle.zip | Eagle board / schematic files]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schematic.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:Components.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR_HV_Programmer_Layout.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR_HV_Programmer_Foto.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== Software, auch für ATtiny4..10, also TPI ===&lt;br /&gt;
&lt;br /&gt;
Open-Source, [http://www.tu-chemnitz.de/~heha/hs/avrpp.zip/ Download].&lt;br /&gt;
Kann direkt ELF-Dateien laden, kein Umweg über &amp;lt;tt&amp;gt;avr-objcopy&amp;lt;/tt&amp;gt; erforderlich.&lt;br /&gt;
Kann EEPROM, Fuses und Lockbits „in einem Rutsch“ der (von-neumannisierten) Hex- oder ELF-Datei&lt;br /&gt;
entnehmen und brennen.&lt;br /&gt;
Sowie die Chip-Signatur aus der Datei (mit &amp;lt;tt&amp;gt;#include &amp;lt;avr/signature.h&amp;gt;&amp;lt;/tt&amp;gt; zu generieren) mit dem Chip vergleichen.&lt;br /&gt;
Bei Verwendung einer ELF-Datei kann die Software die Chip-Signatur von der elf-Section &#039;&#039;&#039;.note.gnu.avr.deviceinfo&#039;&#039;&#039; ermitteln; &amp;lt;tt&amp;gt;#include &amp;lt;avr/signature.h&amp;gt;&amp;lt;/tt&amp;gt; ist dann nicht mehr erforderlich.&lt;br /&gt;
Die Software kennt alle 8-Bit-AVRs.&lt;br /&gt;
Unterstützt direkten (giveio.sys) als auch indirekten (InpOut32.dll) Portzugriff aufs Parallelport sowie eine wahlfreie Port(basis)adresse.&lt;br /&gt;
Läuft von Windows 95 bis Windows 10 sowie Linux; ein PC mit Parallelport (auch als Steckkarte via PCI oder PCIexpress nachgerüstet) ist erforderlich. Parallelports an Docking-Stations funktionieren auch. USB-Parallel-Konverter (für alte Drucker) funktionieren nicht.&lt;br /&gt;
&lt;br /&gt;
Der sechsbeinige Schaltkreis im SOT23-Gehäuse ist wie folgt an die 8-polige Fassung anzuschließen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Pin der Fassung !! Pin des Controllers !! Signalname&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 6 || &amp;lt;span style=&#039;text-decoration:overline&#039;&amp;gt;RESET&amp;lt;/span&amp;gt; = Programmierspannung&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 3 || TPICLK (Takt)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 2 || GND (Bezugspotenzial)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || TPIDATA (Daten, bidirektional)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || 5 || 5P (geschaltet)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Pin 4 des Controllers kann frei bleiben.&lt;br /&gt;
&lt;br /&gt;
==== Fuse bei ATtiny4..10 ====&lt;br /&gt;
&lt;br /&gt;
Diese Mikrocontroller haben laut Dokumentation keine Fuse(s), sondern &#039;&#039;&#039;Configuration Bits&#039;&#039;&#039;.&lt;br /&gt;
avr-gcc trägt diesem Umstand „Rechnung“, dass &amp;lt;tt&amp;gt;#include &amp;amp;lt;avr/fuse.h&amp;amp;gt;&amp;lt;/tt&amp;gt; nicht wie erwartet funktioniert: Die Configuration-Bits sollen laut Linker-Skript in der Sektion &amp;lt;tt&amp;gt;.config&amp;lt;/tt&amp;gt; ankommen, mit der linearen Adresse 0x820000 (die gleiche wie &amp;lt;tt&amp;gt;.fuse&amp;lt;/tt&amp;gt; für die übrigen AVRs).&lt;br /&gt;
In die ELF-Datei setzt man ein einzelnes Configuration-Byte so:&lt;br /&gt;
&lt;br /&gt;
    &#039;&#039;&#039;uint8_t __attribute__((section(&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;&amp;quot;.config&amp;quot;&amp;lt;/span&amp;gt;&#039;&#039;&#039;)))&#039;&#039;&#039; Config=&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;0xFE&amp;lt;/span&amp;gt;;  &#039;&#039;// &amp;lt;span style=&amp;quot;text-decoration:overline&amp;quot;&amp;gt;Reset&amp;lt;/span&amp;gt; = I/O-Pin&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der ELF-Loader vom oben angegebenen &#039;&#039;&#039;avrpp&#039;&#039;&#039; berücksichtigt diese Besonderheit.&lt;br /&gt;
&lt;br /&gt;
==== avr-gcc-Fehler bei ATtiny4 und ATtiny5 ====&lt;br /&gt;
&lt;br /&gt;
Verwendet man&lt;br /&gt;
&lt;br /&gt;
    #include &amp;lt;avr/signature.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verweigert &#039;&#039;&#039;avrpp&#039;&#039;&#039; die Zusammenarbeit mit dem Chip, weil die Signatur nicht passt. Hier liegt der Fehler in den Kopfdateien &amp;lt;tt&amp;gt;avr/tn4.h&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;avr/tn5.h&amp;lt;/tt&amp;gt;, dort sind die Signaturbytes tatsächlich falsch eingetragen.&lt;br /&gt;
Vielleicht liest das jemand vom avr-gcc-Team und packt das Problem an der Wurzel, statt selbst die Dateien korrigieren zu müssen.&lt;br /&gt;
&lt;br /&gt;
=== USB-Hardware ===&lt;br /&gt;
&lt;br /&gt;
Das Verschwinden von Parallelports stellt die größte Hürde für Bastel-Programmiergeräte dar.&lt;br /&gt;
Programmiergeräte mit USB leiden nur dann nicht unter dem Henne-Ei-Problem, wenn der verwendete USB-Mikrocontroller bereits ab Fabrik einen USB-Urlader enthält. Immerhin löst USB das Problem der sonst erforderlichen zusätzlichen Stromversorgung.&lt;br /&gt;
&lt;br /&gt;
Es scheint kein Bastel-USB-Programmiergerät zu geben, welches &#039;&#039;&#039;ISP&#039;&#039;&#039; &#039;&#039;und&#039;&#039; &#039;&#039;&#039;HVSP&#039;&#039;&#039; &#039;&#039;und&#039;&#039; &#039;&#039;&#039;HVPP&#039;&#039;&#039; &#039;&#039;und&#039;&#039; &#039;&#039;&#039;TPI&#039;&#039;&#039; kann! &#039;&#039;&#039;TPI&#039;&#039;&#039; sowohl in Niedervolt- als auch Hochvolt-Ausprägung. Deshalb habe ich hier ein [https://www-user.tu-chemnitz.de/~heha/basteln/pg/avrpp#2 nachbaufähiges Muster] entwickelt. Es basiert auf ATmega16U2, der bereits einen Urlader mitbringt.&lt;br /&gt;
&lt;br /&gt;
Derzeit funktioniert TPI noch nicht. Der Gerätezugriff darauf erfolgt noch nicht mit avrpp.exe sondern &amp;lt;tt&amp;gt;avrdude -c stk500&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;stk500pp&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;stk500hvsp&amp;lt;/tt&amp;gt;. Unterstützung für PIC ist ebenfalls vorgesehen, indem die Programmierspannung einstellbar ist. (Das PIC-Programmierinterface ist identisch zu TPI, bloß ein anderer Name und ein anderes Protokoll.)&lt;br /&gt;
&lt;br /&gt;
=== Betrieb mit SMD-Clip ===&lt;br /&gt;
&lt;br /&gt;
Beim Betrieb dieses Programmiergerätes in Verbindung mit einem 8-poligen SMD-Clip zur Programmierung von &#039;&#039;&#039;ATtiny&amp;lt;i&amp;gt;X&amp;lt;/i&amp;gt;5&#039;&#039;&#039; (SU und SSU = breites und normales SMD-Gehäuse mit &#039;&#039;&#039;e&#039;&#039;&#039; = 1,27 mm) versagt bereits das Auslesen der Device-ID-Bytes, und der Chip wird nicht erkannt.&lt;br /&gt;
Auch das Drosseln der Programmiergeschwindigkeit (bspw. mit &#039;&#039;&#039;-i10&#039;&#039;&#039;) hilft nicht dagegen.&lt;br /&gt;
Ursache sind gegenseitige Störungen auf den vier Datenleitungen, die vom Mikrocontroller selbst verursacht werden und über die Leitungslänge von nur 15 cm nicht mehr genügend von den Widerständen auf dem Programmerboard bedämpft werden.&lt;br /&gt;
Dies führt letztlich zu zusätzlichen Taktflanken auf &#039;&#039;&#039;SCI = PB3&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Abhilfe schafft ein &#039;&#039;&#039;Kondensator 100 pF&#039;&#039;&#039; zwischen &#039;&#039;&#039;Pin 2 = SCI = PB3&#039;&#039;&#039; und &#039;&#039;&#039;Pin 4 = GND&#039;&#039;&#039; &#039;&#039;direkt am zu programmierenden Mikrocontroller&#039;&#039;.&lt;br /&gt;
&amp;lt;small&amp;gt;Alternativ ein &#039;&#039;&#039;Kondensator 4,7 nF&#039;&#039;&#039; zwischen &#039;&#039;&#039;Pin 7 = PB2 = SDO&#039;&#039;&#039; und &#039;&#039;&#039;Pin 4 = GND&#039;&#039;&#039;, ebenfalls &#039;&#039;direkt am zu programmierenden Mikrocontroller&#039;&#039;.&lt;br /&gt;
Andere versuchsweise Kondensatoren, insbesondere zwischen &#039;&#039;&#039;Pin 8 = U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&#039;&#039;&#039; und &#039;&#039;&#039;Pin 4 = GND&#039;&#039;&#039; brachten keinen Erfolg.&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe Problem wird vermutlich auch bei anderen 8-poligen ATtinys und beim Verlängern mit 14-poligen &#039;&#039;&#039;ATtiny&amp;lt;i&amp;gt;X&amp;lt;/i&amp;gt;4&#039;&#039;&#039; auftreten, da gleicher Programmieralgorithmus: „HVSP“ = Hochvolt-serielle Programmierung.&lt;br /&gt;
&lt;br /&gt;
Es ist davon auszugehen, dass eine geeignete Terminierung auf der Programmer-Seite (bspw. Serienwiderstände 100 Ω statt 1 kΩ) das Problem ebenfalls und besser löst.&lt;br /&gt;
&lt;br /&gt;
===siehe auch===&lt;br /&gt;
[http://xray37.de/?Bastelbuch:Lima-SDR:RX Miniatur-Version von ElmChans HV-Programmer]&lt;br /&gt;
&lt;br /&gt;
[[AVR In System Programmer]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader]]&lt;br /&gt;
[[Kategorie:AVR-Boards]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=LED-%22Birnen%22&amp;diff=107154</id>
		<title>LED-&quot;Birnen&quot;</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=LED-%22Birnen%22&amp;diff=107154"/>
		<updated>2024-10-30T13:30:34Z</updated>

		<summary type="html">&lt;p&gt;Heha: Weißraum weg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Beitrag beschreibt Aufbau und Funktion von [[LED]]-basierten Leuchtmitteln (LED-Lampen), umgangssprachlich auch manchmal als &#039;&#039;&#039;&amp;quot;LED-Birnen&amp;quot;&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Anstelle von Glühlampen werden heutzutage nahezu ausschließlich LED-„Birnen“ benutzt. Im Vergleich zu den Anfang des Jahrtausends eingesetzten Kompaktleuchtstofflampen (Quecksilberdampf-Niederdrucklampe) erreichen sie ein noch höheres Energiesparpotenzial ohne Problemabfall zu sein. Dabei erreichen sie die volle Helligkeit sofort nach dem Einschalten, genauso wie Metalldraht-Glühlampen.&lt;br /&gt;
Dabei dominieren Bauformen, die auf leichte Fertigung optimiert sind (plane Leiterplatte und Lichtstreukappe). Für repräsentativen Einsatz (sichtbare Birne) gibt es sogenannte Filamentlampen, deren LED-Nacktchips dicht an dicht auf ein Keramiksubstrat gebondet sind.&lt;br /&gt;
&lt;br /&gt;
== Vergleich mit anderen Leuchtmitteln ==&lt;br /&gt;
Auf den Packungen der Hersteller sind sowohl bei Kompaktleuchtstofflampen, als auch bei LED-Birnen sehr optimistische Werte für die Lebensdauer und die Helligkeit angegeben. &lt;br /&gt;
&lt;br /&gt;
Seit 1. September 2013 ist die [http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=OJ:L:2012:342:0001:0022:DE:PDF DIM II – EU 1194/2012 EU Verordnung] in Kraft, die gewisse Mindestanforderungen an die angegeben Werte gibt. Die Anforderungen werden schrittweise in 3 Stufen verschärft: &lt;br /&gt;
 - Stufe 1: 1. September 2013, &lt;br /&gt;
 - Stufe 2: 1. September 2014&lt;br /&gt;
 - Stufe 3: 1. September 2016&lt;br /&gt;
&lt;br /&gt;
Die Einhaltung wird durch Marktaufsichtsbehörden zu kontrollieren; Man kann sich nur selber überlegen ob eher die Marken oder Noname-Hersteller sich daran halten werden.&lt;br /&gt;
&lt;br /&gt;
=== Lebensdauervergleich===&lt;br /&gt;
Kompaktleuchtstofflampen werden gerne mit bis zu 10.000h - LED-Lampen sogar mit bis zu 50.000h angegeben. &lt;br /&gt;
&lt;br /&gt;
Für LED Lampen fordert die DIM II eine maximale Frühausfallrate von 5% nach 1000h und eine maximale Ausfallrate von 10% nach 6000h. (ab Stufe 1)&lt;br /&gt;
&lt;br /&gt;
Kompaktleuchtstofflampen dürfen maximale 5% Frühausfallrate nach 500h haben und nach 6000h dürfen maximal 50% ausgefallen sein. (Stufe 1; Stufe 3: 70%)&lt;br /&gt;
&lt;br /&gt;
Ein weiterer Punkt ist die alterungsbedingte Lichtstromabnahme: Nach der Richtlinie müssen für Kompaktleuchtstofflampen nach 2000h noch 80% (Stufe 2) bzw 83% nach 2000h und 70% nach 6000h (Stufe 3)&lt;br /&gt;
&lt;br /&gt;
LEDs müssen nach 6000h noch mindestens 80% Lichtstrom haben. (Stufe 2)&lt;br /&gt;
&lt;br /&gt;
Zum Vergleich: 50% der Glühlampen müssten &amp;gt;1000h (Stufe 1), 2000h (Stufe 2) bzw. für Kleinspannungslampen ab Stufe 3 4000h leben.&lt;br /&gt;
Dabei müssen sie bei 3/4 der obigen Lebensdauerforderung noch mindestens 80% der Helligkeit haben. Frühausfallraten wären &amp;lt;5% bei 200h.&lt;br /&gt;
Wären, da bekannterweise die Glühlampen die Anforderungen &lt;br /&gt;
&lt;br /&gt;
=== Leistungsvergleich===&lt;br /&gt;
&lt;br /&gt;
Nach der DIM II ist es vorgeschrieben, dass die Lampen oder die Verpackung den Lichtstromwert in Lumen ausweisen. Damit werden die Leuchtmittel direkt vergleichbar in Punkto Helligkeit. Zusätzlich dürfen die Äquivalenzwerte angegeben werden, wobei die Umrechnung vom Lichtstrom vorgegeben ist. In der Regel benötigen hier dann LED-Lampen &amp;quot;mehr&amp;quot; Lumen als herkömmliche Glühlampen um die selbe Äquivalenzleistung zu haben.&lt;br /&gt;
&lt;br /&gt;
Beispiel: eine GU20 40D 35W Halogen hat ca. 140lm. Die selbe Lichtmenge reicht nur für das Labeln von 25W (Äquivalent). 35W würden 230lm brauchen. (Beispiel von [http://www.lighting.philips.com/pwc_li/main/led/assets/final%20dim2-philips_presentation.pdf hier, Seite 8])&lt;br /&gt;
&lt;br /&gt;
Im direkten Vergleich ist der Lichstrom sinnvoller, da hier die Augenempfindlichkeit schon berücksichtigt ist.&lt;br /&gt;
&lt;br /&gt;
=== Farbwiedergabe- und Lichtfarbenvergleich ===&lt;br /&gt;
&lt;br /&gt;
Auch Anforderungen an die Farbwiedergabe und an die Lichtfarbe werden in der DIM 2 formuliert:&lt;br /&gt;
&lt;br /&gt;
==== Farbwiedergabe ====&lt;br /&gt;
&lt;br /&gt;
Glühlampen haben per Definition einen Farbwiedergabewert Ra=100.&lt;br /&gt;
Für LED und Kompaktleuchtstofflampen sind die Mindestanforderungen Ra&amp;gt;85 (bzw. Ra&amp;gt;65 für Aussen- oder Industrieanwendungen). Gute Lampen gibt es aber auch mit Ra&amp;gt;90.&lt;br /&gt;
&lt;br /&gt;
==== Lichtfarbe ====&lt;br /&gt;
&lt;br /&gt;
Auch die [https://de.wikipedia.org/wiki/Farbtemperatur Lichtfarbe] muss angegeben werden. Hier ist die Einheit Kelvin und je kleiner der Wert desto &amp;quot;wärmer&amp;quot; das Licht. &lt;br /&gt;
&lt;br /&gt;
Normalerweise bekommt man hier nur &amp;quot;warmweiss&amp;quot; oder 3000K. &lt;br /&gt;
Damit sollten die Zeiten vorbei sein wo man vom Chinamann eine 8000K LED-Lampe als warmweiss verkauft bekommt und dann sich über das blaue Licht wundert.&lt;br /&gt;
&lt;br /&gt;
Bei LEDs darf maximale Farbabweichung darf hier nicht mehr als 6 [http://fastvoice.net/2013/09/18/farbkonsistenz-macadam-sdcm-wie-unterschiedlich-leuchten-leds/ Schwellwerteinheiten] sein. Der Wert sollte angegeben sein, gut Lampen erreichen hier weniger als 3 Schwellwerteinheiten.&lt;br /&gt;
&lt;br /&gt;
=== Vor- und Nachteile der LED-Lampen ===&lt;br /&gt;
&lt;br /&gt;
LED-Lampen haben einige Vorteile gegenüber den Kompaktleuchtstofflampen und vor allem gegenüber den Glühfadenlampen: &lt;br /&gt;
&lt;br /&gt;
* Die Energieeffizienz ist in der Regel höher als bei Kompaktleuchststofflampen.&lt;br /&gt;
* Die Kosteneffizienz ist bei einfachen Ausführungen ähnlich gut.&lt;br /&gt;
* Die Lichtfarbe bzw. Farbtemperatur der LED-Lampe ist flexibler und kann auf Wunsch der Glühfadenlampenfarbe besser angepasst werden. Dazu gibt es kaltweiße, warmweiße und auch farbige Ausführungen.&lt;br /&gt;
* Sie enthalten keine leicht freisetzbaren Giftstoffe wie Quecksilber.&lt;br /&gt;
* Erreichen bereits nach wenigen Millisekunden nach dem Einschalten die volle Leuchtkraft.&lt;br /&gt;
* Können eine sehr lange Lebensdauer haben (meist &amp;gt;= 30000 Stunden).&lt;br /&gt;
* Sind nach Lebensende „nur“ als Elektroschrott zu entsorgen — kein Problemabfall wie Energiesparlampen.&lt;br /&gt;
&lt;br /&gt;
Jedoch gibt es auch einige Nachteile:&lt;br /&gt;
&lt;br /&gt;
* bei birnenähnlichen Designs und höheren Leistungen höherer Beschaffungspreis&lt;br /&gt;
* Ähnlich wie bei ESLs befinden sich sehr viel billige Produkte am Markt, bei denen die Elektronik rasch kaputt geht.&lt;br /&gt;
* schlechterer Farbwiedergabeindex R&amp;lt;sub&amp;gt;a&amp;lt;/sub&amp;gt; = 80-95 (Glühbirne als Referenz R&amp;lt;sub&amp;gt;a&amp;lt;/sub&amp;gt; = 100)&lt;br /&gt;
* Bei Billigprodukten oftmals EMV-Probleme (Störungen im Kurzwellenbereich sowie bei DAB+)&lt;br /&gt;
* Sollten nicht in Leuchten verwendet werden, bei denen eine Keramikfassung darauf hindeutet, dass der Sockel heiß werden darf. Anders als die extrem hitzebeständigen Glühlampen sollte der Sockel einer LED-Lampe möglichst kühl bleiben. Daher muss man bei einer liebgewonnenen Leuchte ggf. konstruktive Maßnahmen zur Wärmeabführung treffen, &#039;&#039;trotz&#039;&#039; der viel geringeren Wärmeabgabe von LED-Lampen&lt;br /&gt;
* Problematischer Einsatz in Feuchträumen (Keller, Brunnen, unterirdische Anlagen bspw. der Wasserver- und Entsorgung, Schwimmbad, Tropen — &#039;&#039;nicht&#039;&#039; das heimische Bad): Ersatz durch Kleinspannung erspart empfindliche Elektronik im Feuchtbereich&lt;br /&gt;
&lt;br /&gt;
=== Kostenvergleich von Energiesparlampen ===&lt;br /&gt;
[[Datei:Kostenvergleich-led-esl-birnen.jpg|left|300px|Kostenvergleich]]&lt;br /&gt;
&lt;br /&gt;
Die Grafik zeigt 3 Fälle von Kostenrechnungen für konventionelle Glühlampen, Kompaktleuchtstofflampe (als ESL bezeichnet) und LED-Lampen.&lt;br /&gt;
&lt;br /&gt;
Berechnet werden jeweils die beiden Extremfälle einer Lampe mit niedrigem Preis und hoher Lebensdauer (günstiger Fall) und einer &amp;quot;Montagslampe&amp;quot;, die schnell kaputt geht, trotz hohen Preises. Daraus wird ein wahrscheinlichster Mittelwert (geometrische Mitte) - einmal für geringe und einmal für starke Nutzung gebildet.&lt;br /&gt;
&lt;br /&gt;
Aufgeführt ist auch die Unterscheidung der privaten und geschäftlichen Nutzung, bei der die Beschaffungs- und Wartungszeiten viel stärker zu Buche schlagen, da ein Angestellter bezahlt werden muss. Hier zeigt sich der Vorteil der langen Nutzungsdauer der LED-Lampen besonders deutlich.&lt;br /&gt;
&lt;br /&gt;
Es zeigt sich auch, dass Privatnutzer durchaus noch einen geringen Sparvorteil haben können, wenn sie auf LED-Lampen umrüsten.&lt;br /&gt;
&lt;br /&gt;
Excel für eigene Berechnungen: [http://www.mikrocontroller.net/wikifiles/0/09/Lampenrechner.xls Lampenvergleichsrechner]&lt;br /&gt;
&lt;br /&gt;
Externer Link auf Excel:&lt;br /&gt;
[http://shop.bioledex.de/files/BIOLEDEX-LED-Stromkosten-Rechner.xls Stromkostenrechner]&lt;br /&gt;
&lt;br /&gt;
== Bauformen ==&lt;br /&gt;
=== Hochvolt-LED-Lampen===&lt;br /&gt;
&lt;br /&gt;
Moderne Lampen mit einer Leistung über 5 W enthalten einen Gleichrichter, eine simple elektronische Strombegrenzung sowie eine Kette von LEDs mit einer Gesamtflussspannung von typisch 200 V.&lt;br /&gt;
Die wie Einzelchips aussehenden SMD-LEDs enthalten oftmals mehrere LEDs in Reihe.&lt;br /&gt;
Zudem ist in jedem der SMD-LED eine „Crowbar“ genannte Schaltung aus Z-Diode und Thyristor enthalten, die bei Ausfall (=Unterbrechung) einer Leuchtdiode das Bauteil elektrisch überbrückt, sodass die Kette weiter leuchtet.&lt;br /&gt;
Von Vorteil dieses Designs ist das Fehlen von Funkstörungen.&lt;br /&gt;
Nachteil dieses Designs ist 100-Hz-Flimmern (ungünstig für Kinos) und eine stark nichtsinusförmige Stromaufnahme.&lt;br /&gt;
Auch der Wirkungsgrad ist nicht gerade üppig.&lt;br /&gt;
Gesehen in LED-Panels für Außenbeleuchtung.&lt;br /&gt;
&lt;br /&gt;
Mit nur einen kleinen Speicherdrossel kommen Designs aus, die mit einem Hochsetzsteller einen sinusförmigen Strom durch eine LED-Kette mit einer Gesamtflussspannung von 400 V einprägen. Das 100-Hz-Flimmern ist wesentlich softer. Ein Netzfilter muss die Funkstörungen des Hochsetzstellers unterdrücken.&lt;br /&gt;
&lt;br /&gt;
Für exaktes Gleichlicht ist ein weiterer Energiespeicher nötig, der 10 ms überbrückt. Das ist typischerweise ein Elektrolytkondensator.&lt;br /&gt;
&lt;br /&gt;
=== Niederspannungs-LED-Lampen===&lt;br /&gt;
==== Halogenersatzlampen ====&lt;br /&gt;
Üblicherweise werden Niederspannungs-LED-Lampen an einem dezentralen Transformator betrieben, wie z.B mit 24V. Mit einer geeigneten Vorschaltung zur Spannungsherabsetzung im Lampengehäuse, sind sie auch indirekt an 230V-Netzen benutzbar.&lt;br /&gt;
&lt;br /&gt;
==== Betrieb von Standard-LEDs an 230V ====&lt;br /&gt;
[[Datei:led_230v.png|left|300px|230V-Vorschaltung]]&lt;br /&gt;
Mit der nebenstehenden Schaltung besteht die Möglichkeit, eine normale LED direkt an 230V anzuschließen. Diese Schaltung funktioniert so, dass die Spannung mittels Z-Dioden begrenzt wird; in diesem Fall auf 30V. Diese sind deshalb wichtig, weil die Gleichrichtdioden für hohen Frequenzanteile als Kapazität wirken und der Elko diese nicht aufnehmen kann, wodurch sie direkt auf die LEDs wirken würden. Gfs sollte dem Elko noch eine keramischer Kondensator beigefügt werden.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:Led an 220V.png|left|300px|230V-Schaltung]]&lt;br /&gt;
Statt eines Gleichrichters, kann auch eine Antiparallelschaltung von LEDs verwendet werden. Auch dabei werden beide Halbwellen genutzt. Die Ausfallwahrscheinlichkeit ist geringer, weil weniger Bauteile und vor allem kein Elko genutzt wird. Allerdings muss die Schaltung genau dimensioniert werden, d.h. die strombegrenzende Wirkung des Kondensators ist sehr wichtig. Daher werden zur Sicherheit einige in Serie geschaltet, um das Problem der Alterung oder Defektbildung zu minimieren. Die LEDs leuchten bei dieser Schaltungsform aber etwas dunkler als oben, weil kein konstanter Strom fliessen kann und man mit Rücksicht auf die Lebensdauer der LEDs nicht einfach die Spannung so erhöhen kann, dass der Effektivwert erreicht wird.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{warnung|Spannungen ab 60V sind lebensgefährlich!}}&lt;br /&gt;
&lt;br /&gt;
== Funktion einer LED-Lampe am Beispiel==&lt;br /&gt;
von [[Benutzer:Didi34]]&lt;br /&gt;
=== Aufbau ===&lt;br /&gt;
[[Datei:aufbau.JPG|left|300px]]&lt;br /&gt;
Ich beschreibe in diesem Beitrag den Aufbau eines 4W Philips LED-Leuchtmittels mit E-14 Sockel. Über dem LED-Chip befindet sich ein Glaskolben, der in den weißen Kunststoffsockel geklebt ist. Der Glaskolben ist aus Milchglas, dies dient als Diffusor. Um näher in die Lampe zu kommen, muss der Glaskolben abgenommen werden.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:sockel.JPG|left|300px]]&lt;br /&gt;
Nun sieht man den LED-Chip in der Mitte der Lampe. Über dem Chip befindet sich eine weitere Abdeckung, die abgenommen werden muss. &lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:chip_anschluss.JPG|left|300px]]&lt;br /&gt;
Auf dem nächsten Bild sieht man die Aluminium-Platine, worauf sich der LED-Chip befindet. Die beiden Drähte stellen die Versorgung der LED (300V Gleichspannung) dar. Die Platine besteht aus dem Basismaterial Aluminium, um eine bessere Kühlung zu erreichen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
=== Innenleben ===&lt;br /&gt;
[[Datei:platine_unten.jpg|left|300px]]&lt;br /&gt;
Im Inneren der LED-Lampe befindet sich folgende Platine:&lt;br /&gt;
&lt;br /&gt;
Man sieht nun auf der Platine eine kleine Gleichrichterschaltung. Die Anschlüsse Blau und Braun sind Neutralleiter(N) und Außenleiter(L1) unserer Energieversorgung mit 230V Wechselspannung.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:platine_oben.JPG|left|300px]]&lt;br /&gt;
&lt;br /&gt;
Die beiden Drähte Rot(+) und Schwarz(-) sind die Ausgänge der Schaltung. Sie führen eine Gleichspannung von 300V und werden direkt an der Aluminiumplatine der LED angeschlossen, da die LED für diese Spannung ausgelegt ist. Näheres zur Schaltung:&lt;br /&gt;
[http://www.mikrocontroller.net/articles/LED-Gl%C3%BChbirne#Schaltung/ Schaltung]&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== LED-Chip ====&lt;br /&gt;
[[Datei:ledchip_dunkel.jpg|left|300px]]&lt;br /&gt;
Der LED-Chip besteht aus mehreren in Serie geschalteten LEDs. Die in Serie geschalteten LEDs sind um eine warm-weiße Farbe zu erreichen teils rot und teils weiß. Die LED ist für 300V DC ausgelegt. Auf dem LED-Chip befinden sich zwei weiße und zwei rote LED-Arrays. Die gelbe Schicht der weißen LEDs ist ein Phosphor (also ein Leuchtstoff) der aus dem blauen Licht der Kristalle weißes Licht macht.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:ledchip_hell.jpg|left|300px]]&lt;br /&gt;
Eine weitere Ansicht des LED-Chips im Betrieb unter Spannung.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== Schaltung ====&lt;br /&gt;
[[Datei:netzteilschaltung.png|left|300px|Einfache Gleichrichterschaltung für LEDs]]&lt;br /&gt;
Für den Betieb an Netzspannung wird ein Kondensatornetzteil verwendet, welches aus 230V AC 300V DC macht. Die Schaltung ist mit 1A abgesichert. {{Absatz}}&lt;br /&gt;
&lt;br /&gt;
=== Messung ===&lt;br /&gt;
[[Datei:messung.bmp|left|300px|Messung]]&lt;br /&gt;
Es wurde zwischen dem roten und schwarzen Ausgang der oben genannten Schaltung gemessen. CH1 roter Anschluss CH2 schwarzer Anschluss.&lt;br /&gt;
Die rote Linie am Oszillogramm ist Die Spannung zwischen den beiden Drähten (CH1-CH2).&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
{{warnung|Spannungen ab 60V sind lebensgefährlich!}}&lt;br /&gt;
&lt;br /&gt;
== Interne Links ==&lt;br /&gt;
* [[LED]]&lt;br /&gt;
&lt;br /&gt;
== Externe Links ==&lt;br /&gt;
&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Leuchtdiode Wikipedia: LED]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kompaktleuchtstofflampe Wikipedia: Kompaktleuchtstofflampe]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Glühlampe Wikipedia: Glühbirne ]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kondensatornetzteil Wikipedia: Kondensatornetzteil]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Energiesparende Leuchtmittel Wikipedia: Energiesparende Leuchtmittel allgemein]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]]&lt;br /&gt;
[[Kategorie:Displays und Anzeigen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=LED-%22Birnen%22&amp;diff=107153</id>
		<title>LED-&quot;Birnen&quot;</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=LED-%22Birnen%22&amp;diff=107153"/>
		<updated>2024-10-30T13:27:21Z</updated>

		<summary type="html">&lt;p&gt;Heha: fehlen noch Schaltpläne und Beispiele&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Beitrag beschreibt Aufbau und Funktion von [[LED]]-basierten Leuchtmitteln (LED-Lampen), umgangssprachlich auch manchmal als &#039;&#039;&#039;&amp;quot;LED-Birnen&amp;quot;&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Anstelle von Glühlampen werden heutzutage nahezu ausschließlich LED-„Birnen“ benutzt. Im Vergleich zu den Anfang des Jahrtausends eingesetzten Kompaktleuchtstofflampen (Quecksilberdampf-Niederdrucklampe) erreichen sie ein noch höheres Energiesparpotenzial ohne Problemabfall zu sein. Dabei erreichen sie die volle Helligkeit sofort nach dem Einschalten, genauso wie Metalldraht-Glühlampen.&lt;br /&gt;
Dabei dominieren Bauformen, die auf leichte Fertigung optimiert sind (plane Leiterplatte und Lichtstreukappe). Für repräsentativen Einsatz (sichtbare Birne) gibt es sogenannte Filamentlampen, deren LED-Nacktchips dicht an dicht auf ein Keramiksubstrat gebondet sind.&lt;br /&gt;
&lt;br /&gt;
== Vergleich mit anderen Leuchtmitteln ==&lt;br /&gt;
Auf den Packungen der Hersteller sind sowohl bei Kompaktleuchtstofflampen, als auch bei LED-Birnen sehr optimistische Werte für die Lebensdauer und die Helligkeit angegeben. &lt;br /&gt;
&lt;br /&gt;
Seit 1. September 2013 ist die [http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=OJ:L:2012:342:0001:0022:DE:PDF DIM II – EU 1194/2012 EU Verordnung] in Kraft, die gewisse Mindestanforderungen an die angegeben Werte gibt. Die Anforderungen werden schrittweise in 3 Stufen verschärft: &lt;br /&gt;
 - Stufe 1: 1. September 2013, &lt;br /&gt;
 - Stufe 2: 1. September 2014&lt;br /&gt;
 - Stufe 3: 1. September 2016&lt;br /&gt;
&lt;br /&gt;
Die Einhaltung wird durch Marktaufsichtsbehörden zu kontrollieren; Man kann sich nur selber überlegen ob eher die Marken oder Noname-Hersteller sich daran halten werden.&lt;br /&gt;
&lt;br /&gt;
=== Lebensdauervergleich===&lt;br /&gt;
Kompaktleuchtstofflampen werden gerne mit bis zu 10.000h - LED-Lampen sogar mit bis zu 50.000h angegeben. &lt;br /&gt;
&lt;br /&gt;
Für LED Lampen fordert die DIM II eine maximale Frühausfallrate von 5% nach 1000h und eine maximale Ausfallrate von 10% nach 6000h. (ab Stufe 1)&lt;br /&gt;
&lt;br /&gt;
Kompaktleuchtstofflampen dürfen maximale 5% Frühausfallrate nach 500h haben und nach 6000h dürfen maximal 50% ausgefallen sein. (Stufe 1; Stufe 3: 70%)&lt;br /&gt;
&lt;br /&gt;
Ein weiterer Punkt ist die alterungsbedingte Lichtstromabnahme: Nach der Richtlinie müssen für Kompaktleuchtstofflampen nach 2000h noch 80% (Stufe 2) bzw 83% nach 2000h und 70% nach 6000h (Stufe 3)&lt;br /&gt;
&lt;br /&gt;
LEDs müssen nach 6000h noch mindestens 80% Lichtstrom haben. (Stufe 2)&lt;br /&gt;
&lt;br /&gt;
Zum Vergleich: 50% der Glühlampen müssten &amp;gt;1000h (Stufe 1), 2000h (Stufe 2) bzw. für Kleinspannungslampen ab Stufe 3 4000h leben.&lt;br /&gt;
Dabei müssen sie bei 3/4 der obigen Lebensdauerforderung noch mindestens 80% der Helligkeit haben. Frühausfallraten wären &amp;lt;5% bei 200h.&lt;br /&gt;
Wären, da bekannterweise die Glühlampen die Anforderungen &lt;br /&gt;
&lt;br /&gt;
=== Leistungsvergleich===&lt;br /&gt;
&lt;br /&gt;
Nach der DIM II ist es vorgeschrieben, dass die Lampen oder die Verpackung den Lichtstromwert in Lumen ausweisen. Damit werden die Leuchtmittel direkt vergleichbar in Punkto Helligkeit. Zusätzlich dürfen die Äquivalenzwerte angegeben werden, wobei die Umrechnung vom Lichtstrom vorgegeben ist. In der Regel benötigen hier dann LED-Lampen &amp;quot;mehr&amp;quot; Lumen als herkömmliche Glühlampen um die selbe Äquivalenzleistung zu haben.&lt;br /&gt;
&lt;br /&gt;
Beispiel: eine GU20 40D 35W Halogen hat ca. 140lm. Die selbe Lichtmenge reicht nur für das Labeln von 25W (Äquivalent). 35W würden 230lm brauchen. (Beispiel von [http://www.lighting.philips.com/pwc_li/main/led/assets/final%20dim2-philips_presentation.pdf hier, Seite 8])&lt;br /&gt;
&lt;br /&gt;
Im direkten Vergleich ist der Lichstrom sinnvoller, da hier die Augenempfindlichkeit schon berücksichtigt ist.&lt;br /&gt;
&lt;br /&gt;
=== Farbwiedergabe- und Lichtfarbenvergleich ===&lt;br /&gt;
&lt;br /&gt;
Auch Anforderungen an die Farbwiedergabe und an die Lichtfarbe werden in der DIM 2 formuliert:&lt;br /&gt;
&lt;br /&gt;
==== Farbwiedergabe ====&lt;br /&gt;
&lt;br /&gt;
Glühlampen haben per Definition einen Farbwiedergabewert Ra=100.&lt;br /&gt;
Für LED und Kompaktleuchtstofflampen sind die Mindestanforderungen Ra&amp;gt;85 (bzw. Ra&amp;gt;65 für Aussen- oder Industrieanwendungen). Gute Lampen gibt es aber auch mit Ra&amp;gt;90.&lt;br /&gt;
&lt;br /&gt;
==== Lichtfarbe ====&lt;br /&gt;
&lt;br /&gt;
Auch die [https://de.wikipedia.org/wiki/Farbtemperatur Lichtfarbe] muss angegeben werden. Hier ist die Einheit Kelvin und je kleiner der Wert desto &amp;quot;wärmer&amp;quot; das Licht. &lt;br /&gt;
&lt;br /&gt;
Normalerweise bekommt man hier nur &amp;quot;warmweiss&amp;quot; oder 3000K. &lt;br /&gt;
Damit sollten die Zeiten vorbei sein wo man vom Chinamann eine 8000K LED-Lampe als warmweiss verkauft bekommt und dann sich über das blaue Licht wundert.&lt;br /&gt;
&lt;br /&gt;
Bei LEDs darf maximale Farbabweichung darf hier nicht mehr als 6 [http://fastvoice.net/2013/09/18/farbkonsistenz-macadam-sdcm-wie-unterschiedlich-leuchten-leds/ Schwellwerteinheiten] sein. Der Wert sollte angegeben sein, gut Lampen erreichen hier weniger als 3 Schwellwerteinheiten.&lt;br /&gt;
&lt;br /&gt;
=== Vor- und Nachteile der LED-Lampen ===&lt;br /&gt;
&lt;br /&gt;
LED-Lampen haben einige Vorteile gegenüber den Kompaktleuchtstofflampen und vor allem gegenüber den Glühfadenlampen: &lt;br /&gt;
&lt;br /&gt;
* Die Energieeffizienz ist in der Regel höher als bei Kompaktleuchststofflampen.&lt;br /&gt;
* Die Kosteneffizienz ist bei einfachen Ausführungen ähnlich gut.&lt;br /&gt;
* Die Lichtfarbe bzw. Farbtemperatur der LED-Lampe ist flexibler und kann auf Wunsch der Glühfadenlampenfarbe besser angepasst werden. Dazu gibt es kaltweiße, warmweiße und auch farbige Ausführungen.&lt;br /&gt;
* Sie enthalten keine leicht freisetzbaren Giftstoffe wie Quecksilber.&lt;br /&gt;
* Erreichen bereits nach wenigen Millisekunden nach dem Einschalten die volle Leuchtkraft.&lt;br /&gt;
* Können eine sehr lange Lebensdauer haben (meist &amp;gt;= 30000 Stunden).&lt;br /&gt;
* Sind nach Lebensende „nur“ als Elektroschrott zu entsorgen — kein Problemabfall wie Energiesparlampen.&lt;br /&gt;
&lt;br /&gt;
Jedoch gibt es auch einige Nachteile:&lt;br /&gt;
&lt;br /&gt;
* bei birnenähnlichen Designs und höheren Leistungen höherer Beschaffungspreis&lt;br /&gt;
* Ähnlich wie bei ESLs befinden sich sehr viel billige Produkte am Markt, bei denen die Elektronik rasch kaputt geht.&lt;br /&gt;
* schlechterer Farbwiedergabeindex R&amp;lt;sub&amp;gt;a&amp;lt;/sub&amp;gt; = 80-95 (Glühbirne als Referenz R&amp;lt;sub&amp;gt;a&amp;lt;/sub&amp;gt; = 100)&lt;br /&gt;
* Bei Billigprodukten oftmals EMV-Probleme (Störungen im Kurzwellenbereich sowie bei DAB+)&lt;br /&gt;
* Sollten nicht in Leuchten verwendet werden, bei denen eine Keramikfassung darauf hindeutet, dass der Sockel heiß werden darf. Anders als die extrem hitzebeständigen Glühlampen sollte der Sockel einer LED-Lampe möglichst kühl bleiben. Daher muss man bei einer liebgewonnenen Leuchte ggf. konstruktive Maßnahmen zur Wärmeabführung treffen, &#039;&#039;trotz&#039;&#039; der viel geringeren Wärmeabgabe von LED-Lampen&lt;br /&gt;
* Problematischer Einsatz in Feuchträumen (Keller, Brunnen, unterirdische Anlagen bspw. der Wasserver- und Entsorgung, Schwimmbad, Tropen — &#039;&#039;nicht&#039;&#039; das heimische Bad): Ersatz durch Kleinspannung erspart empfindliche Elektronik im Feuchtbereich&lt;br /&gt;
&lt;br /&gt;
=== Kostenvergleich von Energiesparlampen ===&lt;br /&gt;
[[Datei:Kostenvergleich-led-esl-birnen.jpg|left|300px|Kostenvergleich]]&lt;br /&gt;
&lt;br /&gt;
Die Grafik zeigt 3 Fälle von Kostenrechnungen für konventionelle Glühlampen, Kompaktleuchtstofflampe (als ESL bezeichnet) und LED-Lampen.&lt;br /&gt;
&lt;br /&gt;
Berechnet werden jeweils die beiden Extremfälle einer Lampe mit niedrigem Preis und hoher Lebensdauer (günstiger Fall) und einer &amp;quot;Montagslampe&amp;quot;, die schnell kaputt geht, trotz hohen Preises. Daraus wird ein wahrscheinlichster Mittelwert (geometrische Mitte) - einmal für geringe und einmal für starke Nutzung gebildet.&lt;br /&gt;
&lt;br /&gt;
Aufgeführt ist auch die Unterscheidung der privaten und geschäftlichen Nutzung, bei der die Beschaffungs- und Wartungszeiten viel stärker zu Buche schlagen, da ein Angestellter bezahlt werden muss. Hier zeigt sich der Vorteil der langen Nutzungsdauer der LED-Lampen besonders deutlich.&lt;br /&gt;
&lt;br /&gt;
Es zeigt sich auch, dass Privatnutzer durchaus noch einen geringen Sparvorteil haben können, wenn sie auf LED-Lampen umrüsten.&lt;br /&gt;
&lt;br /&gt;
Excel für eigene Berechnungen: [http://www.mikrocontroller.net/wikifiles/0/09/Lampenrechner.xls Lampenvergleichsrechner]&lt;br /&gt;
&lt;br /&gt;
Externer Link auf Excel:&lt;br /&gt;
[http://shop.bioledex.de/files/BIOLEDEX-LED-Stromkosten-Rechner.xls Stromkostenrechner]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
== Bauformen ==&lt;br /&gt;
=== Hochvolt-LED-Lampen===&lt;br /&gt;
&lt;br /&gt;
Moderne Lampen mit einer Leistung über 5 W enthalten einen Gleichrichter, eine simple elektronische Strombegrenzung sowie eine Kette von LEDs mit einer Gesamtflussspannung von typisch 200 V.&lt;br /&gt;
Die wie Einzelchips aussehenden SMD-LEDs enthalten oftmals mehrere LEDs in Reihe.&lt;br /&gt;
Zudem ist in jedem der SMD-LED eine „Crowbar“ genannte Schaltung aus Z-Diode und Thyristor enthalten, die bei Ausfall (=Unterbrechung) einer Leuchtdiode das Bauteil elektrisch überbrückt, sodass die Kette weiter leuchtet.&lt;br /&gt;
Von Vorteil dieses Designs ist das Fehlen von Funkstörungen.&lt;br /&gt;
Nachteil dieses Designs ist 100-Hz-Flimmern (ungünstig für Kinos) und eine stark nichtsinusförmige Stromaufnahme.&lt;br /&gt;
Auch der Wirkungsgrad ist nicht gerade üppig.&lt;br /&gt;
Gesehen in LED-Panels für Außenbeleuchtung.&lt;br /&gt;
&lt;br /&gt;
Mit nur einen kleinen Speicherdrossel kommen Designs aus, die mit einem Hochsetzsteller einen sinusförmigen Strom durch eine LED-Kette mit einer Gesamtflussspannung von 400 V einprägen. Das 100-Hz-Flimmern ist wesentlich softer. Ein Netzfilter muss die Funkstörungen des Hochsetzstellers unterdrücken.&lt;br /&gt;
&lt;br /&gt;
Für exaktes Gleichlicht ist ein weiterer Energiespeicher nötig, der 10 ms überbrückt. Das ist typischerweise ein Elektrolytkondensator.&lt;br /&gt;
&lt;br /&gt;
=== Niederspannungs-LED-Lampen===&lt;br /&gt;
==== Halogenersatzlampen ====&lt;br /&gt;
Üblicherweise werden Niederspannungs-LED-Lampen an einem dezentralen Transformator betrieben, wie z.B mit 24V. Mit einer geeigneten Vorschaltung zur Spannungsherabsetzung im Lampengehäuse, sind sie auch indirekt an 230V-Netzen benutzbar.&lt;br /&gt;
&lt;br /&gt;
==== Betrieb von Standard-LEDs an 230V ====&lt;br /&gt;
[[Datei:led_230v.png|left|300px|230V-Vorschaltung]]&lt;br /&gt;
Mit der nebenstehenden Schaltung besteht die Möglichkeit, eine normale LED direkt an 230V anzuschließen. Diese Schaltung funktioniert so, dass die Spannung mittels Z-Dioden begrenzt wird; in diesem Fall auf 30V. Diese sind deshalb wichtig, weil die Gleichrichtdioden für hohen Frequenzanteile als Kapazität wirken und der Elko diese nicht aufnehmen kann, wodurch sie direkt auf die LEDs wirken würden. Gfs sollte dem Elko noch eine keramischer Kondensator beigefügt werden.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:Led an 220V.png|left|300px|230V-Schaltung]]&lt;br /&gt;
Statt eines Gleichrichters, kann auch eine Antiparallelschaltung von LEDs verwendet werden. Auch dabei werden beide Halbwellen genutzt. Die Ausfallwahrscheinlichkeit ist geringer, weil weniger Bauteile und vor allem kein Elko genutzt wird. Allerdings muss die Schaltung genau dimensioniert werden, d.h. die strombegrenzende Wirkung des Kondensators ist sehr wichtig. Daher werden zur Sicherheit einige in Serie geschaltet, um das Problem der Alterung oder Defektbildung zu minimieren. Die LEDs leuchten bei dieser Schaltungsform aber etwas dunkler als oben, weil kein konstanter Strom fliessen kann und man mit Rücksicht auf die Lebensdauer der LEDs nicht einfach die Spannung so erhöhen kann, dass der Effektivwert erreicht wird.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{warnung|Spannungen ab 60V sind lebensgefährlich!}}&lt;br /&gt;
&lt;br /&gt;
== Funktion einer LED-Lampe am Beispiel==&lt;br /&gt;
von [[Benutzer:Didi34]]&lt;br /&gt;
=== Aufbau ===&lt;br /&gt;
[[Datei:aufbau.JPG|left|300px]]&lt;br /&gt;
Ich beschreibe in diesem Beitrag den Aufbau eines 4W Philips LED-Leuchtmittels mit E-14 Sockel. Über dem LED-Chip befindet sich ein Glaskolben, der in den weißen Kunststoffsockel geklebt ist. Der Glaskolben ist aus Milchglas, dies dient als Diffusor. Um näher in die Lampe zu kommen, muss der Glaskolben abgenommen werden.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:sockel.JPG|left|300px]]&lt;br /&gt;
Nun sieht man den LED-Chip in der Mitte der Lampe. Über dem Chip befindet sich eine weitere Abdeckung, die abgenommen werden muss. &lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:chip_anschluss.JPG|left|300px]]&lt;br /&gt;
Auf dem nächsten Bild sieht man die Aluminium-Platine, worauf sich der LED-Chip befindet. Die beiden Drähte stellen die Versorgung der LED (300V Gleichspannung) dar. Die Platine besteht aus dem Basismaterial Aluminium, um eine bessere Kühlung zu erreichen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
=== Innenleben ===&lt;br /&gt;
[[Datei:platine_unten.jpg|left|300px]]&lt;br /&gt;
Im Inneren der LED-Lampe befindet sich folgende Platine:&lt;br /&gt;
&lt;br /&gt;
Man sieht nun auf der Platine eine kleine Gleichrichterschaltung. Die Anschlüsse Blau und Braun sind Neutralleiter(N) und Außenleiter(L1) unserer Energieversorgung mit 230V Wechselspannung.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:platine_oben.JPG|left|300px]]&lt;br /&gt;
&lt;br /&gt;
Die beiden Drähte Rot(+) und Schwarz(-) sind die Ausgänge der Schaltung. Sie führen eine Gleichspannung von 300V und werden direkt an der Aluminiumplatine der LED angeschlossen, da die LED für diese Spannung ausgelegt ist. Näheres zur Schaltung:&lt;br /&gt;
[http://www.mikrocontroller.net/articles/LED-Gl%C3%BChbirne#Schaltung/ Schaltung]&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== LED-Chip ====&lt;br /&gt;
[[Datei:ledchip_dunkel.jpg|left|300px]]&lt;br /&gt;
Der LED-Chip besteht aus mehreren in Serie geschalteten LEDs. Die in Serie geschalteten LEDs sind um eine warm-weiße Farbe zu erreichen teils rot und teils weiß. Die LED ist für 300V DC ausgelegt. Auf dem LED-Chip befinden sich zwei weiße und zwei rote LED-Arrays. Die gelbe Schicht der weißen LEDs ist ein Phosphor (also ein Leuchtstoff) der aus dem blauen Licht der Kristalle weißes Licht macht.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:ledchip_hell.jpg|left|300px]]&lt;br /&gt;
Eine weitere Ansicht des LED-Chips im Betrieb unter Spannung.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== Schaltung ====&lt;br /&gt;
[[Datei:netzteilschaltung.png|left|300px|Einfache Gleichrichterschaltung für LEDs]]&lt;br /&gt;
Für den Betieb an Netzspannung wird ein Kondensatornetzteil verwendet, welches aus 230V AC 300V DC macht. Die Schaltung ist mit 1A abgesichert. {{Absatz}}&lt;br /&gt;
&lt;br /&gt;
=== Messung ===&lt;br /&gt;
[[Datei:messung.bmp|left|300px|Messung]]&lt;br /&gt;
Es wurde zwischen dem roten und schwarzen Ausgang der oben genannten Schaltung gemessen. CH1 roter Anschluss CH2 schwarzer Anschluss.&lt;br /&gt;
Die rote Linie am Oszillogramm ist Die Spannung zwischen den beiden Drähten (CH1-CH2).&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
{{warnung|Spannungen ab 60V sind lebensgefährlich!}}&lt;br /&gt;
&lt;br /&gt;
== Interne Links ==&lt;br /&gt;
* [[LED]]&lt;br /&gt;
&lt;br /&gt;
== Externe Links ==&lt;br /&gt;
&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Leuchtdiode Wikipedia: LED]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kompaktleuchtstofflampe Wikipedia: Kompaktleuchtstofflampe]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Glühlampe Wikipedia: Glühbirne ]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kondensatornetzteil Wikipedia: Kondensatornetzteil]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Energiesparende Leuchtmittel Wikipedia: Energiesparende Leuchtmittel allgemein]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]]&lt;br /&gt;
[[Kategorie:Displays und Anzeigen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=LED-%22Birnen%22&amp;diff=107152</id>
		<title>LED-&quot;Birnen&quot;</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=LED-%22Birnen%22&amp;diff=107152"/>
		<updated>2024-10-30T13:09:02Z</updated>

		<summary type="html">&lt;p&gt;Heha: Wärmeabfuhr ist nicht bei jedem Leuchtendesign vorgesehen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Beitrag beschreibt Aufbau und Funktion von [[LED]]-basierten Leuchtmitteln (LED-Lampen), umgangssprachlich auch manchmal als &#039;&#039;&#039;&amp;quot;LED-Birnen&amp;quot;&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Anstelle von Glühlampen werden heutzutage nahezu ausschließlich LED-„Birnen“ benutzt. Im Vergleich zu den Anfang des Jahrtausends eingesetzten Kompaktleuchtstofflampen (Quecksilberdampf-Niederdrucklampe) erreichen sie ein noch höheres Energiesparpotenzial ohne Problemabfall zu sein. Dabei erreichen sie die volle Helligkeit sofort nach dem Einschalten, genauso wie Metalldraht-Glühlampen.&lt;br /&gt;
Dabei dominieren Bauformen, die auf leichte Fertigung optimiert sind (plane Leiterplatte und Lichtstreukappe). Für repräsentativen Einsatz (sichtbare Birne) gibt es sogenannte Filamentlampen, deren LED-Nacktchips dicht an dicht auf ein Keramiksubstrat gebondet sind.&lt;br /&gt;
&lt;br /&gt;
== Vergleich mit anderen Leuchtmitteln ==&lt;br /&gt;
Auf den Packungen der Hersteller sind sowohl bei Kompaktleuchtstofflampen, als auch bei LED-Birnen sehr optimistische Werte für die Lebensdauer und die Helligkeit angegeben. &lt;br /&gt;
&lt;br /&gt;
Seit 1. September 2013 ist die [http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=OJ:L:2012:342:0001:0022:DE:PDF DIM II – EU 1194/2012 EU Verordnung] in Kraft, die gewisse Mindestanforderungen an die angegeben Werte gibt. Die Anforderungen werden schrittweise in 3 Stufen verschärft: &lt;br /&gt;
 - Stufe 1: 1. September 2013, &lt;br /&gt;
 - Stufe 2: 1. September 2014&lt;br /&gt;
 - Stufe 3: 1. September 2016&lt;br /&gt;
&lt;br /&gt;
Die Einhaltung wird durch Marktaufsichtsbehörden zu kontrollieren; Man kann sich nur selber überlegen ob eher die Marken oder Noname-Hersteller sich daran halten werden.&lt;br /&gt;
&lt;br /&gt;
=== Lebensdauervergleich===&lt;br /&gt;
Kompaktleuchtstofflampen werden gerne mit bis zu 10.000h - LED-Lampen sogar mit bis zu 50.000h angegeben. &lt;br /&gt;
&lt;br /&gt;
Für LED Lampen fordert die DIM II eine maximale Frühausfallrate von 5% nach 1000h und eine maximale Ausfallrate von 10% nach 6000h. (ab Stufe 1)&lt;br /&gt;
&lt;br /&gt;
Kompaktleuchtstofflampen dürfen maximale 5% Frühausfallrate nach 500h haben und nach 6000h dürfen maximal 50% ausgefallen sein. (Stufe 1; Stufe 3: 70%)&lt;br /&gt;
&lt;br /&gt;
Ein weiterer Punkt ist die alterungsbedingte Lichtstromabnahme: Nach der Richtlinie müssen für Kompaktleuchtstofflampen nach 2000h noch 80% (Stufe 2) bzw 83% nach 2000h und 70% nach 6000h (Stufe 3)&lt;br /&gt;
&lt;br /&gt;
LEDs müssen nach 6000h noch mindestens 80% Lichtstrom haben. (Stufe 2)&lt;br /&gt;
&lt;br /&gt;
Zum Vergleich: 50% der Glühlampen müssten &amp;gt;1000h (Stufe 1), 2000h (Stufe 2) bzw. für Kleinspannungslampen ab Stufe 3 4000h leben.&lt;br /&gt;
Dabei müssen sie bei 3/4 der obigen Lebensdauerforderung noch mindestens 80% der Helligkeit haben. Frühausfallraten wären &amp;lt;5% bei 200h.&lt;br /&gt;
Wären, da bekannterweise die Glühlampen die Anforderungen &lt;br /&gt;
&lt;br /&gt;
=== Leistungsvergleich===&lt;br /&gt;
&lt;br /&gt;
Nach der DIM II ist es vorgeschrieben, dass die Lampen oder die Verpackung den Lichtstromwert in Lumen ausweisen. Damit werden die Leuchtmittel direkt vergleichbar in Punkto Helligkeit. Zusätzlich dürfen die Äquivalenzwerte angegeben werden, wobei die Umrechnung vom Lichtstrom vorgegeben ist. In der Regel benötigen hier dann LED-Lampen &amp;quot;mehr&amp;quot; Lumen als herkömmliche Glühlampen um die selbe Äquivalenzleistung zu haben.&lt;br /&gt;
&lt;br /&gt;
Beispiel: eine GU20 40D 35W Halogen hat ca. 140lm. Die selbe Lichtmenge reicht nur für das Labeln von 25W (Äquivalent). 35W würden 230lm brauchen. (Beispiel von [http://www.lighting.philips.com/pwc_li/main/led/assets/final%20dim2-philips_presentation.pdf hier, Seite 8])&lt;br /&gt;
&lt;br /&gt;
Im direkten Vergleich ist der Lichstrom sinnvoller, da hier die Augenempfindlichkeit schon berücksichtigt ist.&lt;br /&gt;
&lt;br /&gt;
=== Farbwiedergabe- und Lichtfarbenvergleich ===&lt;br /&gt;
&lt;br /&gt;
Auch Anforderungen an die Farbwiedergabe und an die Lichtfarbe werden in der DIM 2 formuliert:&lt;br /&gt;
&lt;br /&gt;
==== Farbwiedergabe ====&lt;br /&gt;
&lt;br /&gt;
Glühlampen haben per Definition einen Farbwiedergabewert Ra=100.&lt;br /&gt;
Für LED und Kompaktleuchtstofflampen sind die Mindestanforderungen Ra&amp;gt;85 (bzw. Ra&amp;gt;65 für Aussen- oder Industrieanwendungen). Gute Lampen gibt es aber auch mit Ra&amp;gt;90.&lt;br /&gt;
&lt;br /&gt;
==== Lichtfarbe ====&lt;br /&gt;
&lt;br /&gt;
Auch die [https://de.wikipedia.org/wiki/Farbtemperatur Lichtfarbe] muss angegeben werden. Hier ist die Einheit Kelvin und je kleiner der Wert desto &amp;quot;wärmer&amp;quot; das Licht. &lt;br /&gt;
&lt;br /&gt;
Normalerweise bekommt man hier nur &amp;quot;warmweiss&amp;quot; oder 3000K. &lt;br /&gt;
Damit sollten die Zeiten vorbei sein wo man vom Chinamann eine 8000K LED-Lampe als warmweiss verkauft bekommt und dann sich über das blaue Licht wundert.&lt;br /&gt;
&lt;br /&gt;
Bei LEDs darf maximale Farbabweichung darf hier nicht mehr als 6 [http://fastvoice.net/2013/09/18/farbkonsistenz-macadam-sdcm-wie-unterschiedlich-leuchten-leds/ Schwellwerteinheiten] sein. Der Wert sollte angegeben sein, gut Lampen erreichen hier weniger als 3 Schwellwerteinheiten.&lt;br /&gt;
&lt;br /&gt;
=== Vor- und Nachteile der LED-Lampen ===&lt;br /&gt;
&lt;br /&gt;
LED-Lampen haben einige Vorteile gegenüber den Kompaktleuchtstofflampen und vor allem gegenüber den Glühfadenlampen: &lt;br /&gt;
&lt;br /&gt;
* Die Energieeffizienz ist in der Regel höher als bei Kompaktleuchststofflampen.&lt;br /&gt;
* Die Kosteneffizienz ist bei einfachen Ausführungen ähnlich gut.&lt;br /&gt;
* Die Lichtfarbe bzw. Farbtemperatur der LED-Lampe ist flexibler und kann auf Wunsch der Glühfadenlampenfarbe besser angepasst werden. Dazu gibt es kaltweiße, warmweiße und auch farbige Ausführungen.&lt;br /&gt;
* Sie enthalten keine leicht freisetzbaren Giftstoffe wie Quecksilber.&lt;br /&gt;
* Erreichen bereits nach wenigen Millisekunden nach dem Einschalten die volle Leuchtkraft.&lt;br /&gt;
* Können eine sehr lange Lebensdauer haben (meist &amp;gt;= 30000 Stunden).&lt;br /&gt;
* Sind nach Lebensende „nur“ als Elektroschrott zu entsorgen — kein Problemabfall wie Energiesparlampen.&lt;br /&gt;
&lt;br /&gt;
Jedoch gibt es auch einige Nachteile:&lt;br /&gt;
&lt;br /&gt;
* bei birnenähnlichen Designs und höheren Leistungen höherer Beschaffungspreis&lt;br /&gt;
* Ähnlich wie bei ESLs befinden sich sehr viel billige Produkte am Markt, bei denen die Elektronik rasch kaputt geht.&lt;br /&gt;
* schlechterer Farbwiedergabeindex R&amp;lt;sub&amp;gt;a&amp;lt;/sub&amp;gt; = 80-95 (Glühbirne als Referenz R&amp;lt;sub&amp;gt;a&amp;lt;/sub&amp;gt; = 100)&lt;br /&gt;
* Bei Billigprodukten oftmals EMV-Probleme (Störungen im Kurzwellenbereich sowie bei DAB+)&lt;br /&gt;
* Sollten nicht in Leuchten verwendet werden, bei denen eine Keramikfassung darauf hindeutet, dass der Sockel heiß werden darf. Anders als die extrem hitzebeständigen Glühlampen sollte der Sockel einer LED-Lampe möglichst kühl bleiben. Daher muss man bei einer liebgewonnenen Leuchte ggf. konstruktive Maßnahmen zur Wärmeabführung treffen, &#039;&#039;trotz&#039;&#039; der viel geringeren Wärmeabgabe von LED-Lampen&lt;br /&gt;
* Problematischer Einsatz in Feuchträumen (Keller, Brunnen, unterirdische Anlagen bspw. der Wasserver- und Entsorgung, Schwimmbad, Tropen — &#039;&#039;nicht&#039;&#039; das heimische Bad): Ersatz durch Kleinspannung erspart empfindliche Elektronik im Feuchtbereich&lt;br /&gt;
&lt;br /&gt;
=== Kostenvergleich von Energiesparlampen ===&lt;br /&gt;
[[Datei:Kostenvergleich-led-esl-birnen.jpg|left|300px|Kostenvergleich]]&lt;br /&gt;
&lt;br /&gt;
Die Grafik zeigt 3 Fälle von Kostenrechnungen für konventionelle Glühlampen, Kompaktleuchtstofflampe (als ESL bezeichnet) und LED-Lampen.&lt;br /&gt;
&lt;br /&gt;
Berechnet werden jeweils die beiden Extremfälle einer Lampe mit niedrigem Preis und hoher Lebensdauer (günstiger Fall) und einer &amp;quot;Montagslampe&amp;quot;, die schnell kaputt geht, trotz hohen Preises. Daraus wird ein wahrscheinlichster Mittelwert (geometrische Mitte) - einmal für geringe und einmal für starke Nutzung gebildet.&lt;br /&gt;
&lt;br /&gt;
Aufgeführt ist auch die Unterscheidung der privaten und geschäftlichen Nutzung, bei der die Beschaffungs- und Wartungszeiten viel stärker zu Buche schlagen, da ein Angestellter bezahlt werden muss. Hier zeigt sich der Vorteil der langen Nutzungsdauer der LED-Lampen besonders deutlich.&lt;br /&gt;
&lt;br /&gt;
Es zeigt sich auch, dass Privatnutzer durchaus noch einen geringen Sparvorteil haben können, wenn sie auf LED-Lampen umrüsten.&lt;br /&gt;
&lt;br /&gt;
Excel für eigene Berechnungen: [http://www.mikrocontroller.net/wikifiles/0/09/Lampenrechner.xls Lampenvergleichsrechner]&lt;br /&gt;
&lt;br /&gt;
Externer Link auf Excel:&lt;br /&gt;
[http://shop.bioledex.de/files/BIOLEDEX-LED-Stromkosten-Rechner.xls Stromkostenrechner]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
== Bauformen ==&lt;br /&gt;
=== Hochvolt-LED-Lampen===&lt;br /&gt;
&lt;br /&gt;
=== Niederspannungs-LED-Lampen===&lt;br /&gt;
==== Halogenersatzlampen ====&lt;br /&gt;
Üblicherweise werden Niederspannungs-LED-Lampen an einem dezentralen Transformator betrieben, wie z.B mit 24V. Mit einer geeigneten Vorschaltung zur Spannungsherabsetzung im Lampengehäuse, sind sie auch indirekt an 230V-Netzen benutzbar.&lt;br /&gt;
&lt;br /&gt;
==== Betrieb von Standard-LEDs an 230V ====&lt;br /&gt;
[[Datei:led_230v.png|left|300px|230V-Vorschaltung]]&lt;br /&gt;
Mit der nebenstehenden Schaltung besteht die Möglichkeit, eine normale LED direkt an 230V anzuschließen. Diese Schaltung funktioniert so, dass die Spannung mittels Z-Dioden begrenzt wird; in diesem Fall auf 30V. Diese sind deshalb wichtig, weil die Gleichrichtdioden für hohen Frequenzanteile als Kapazität wirken und der Elko diese nicht aufnehmen kann, wodurch sie direkt auf die LEDs wirken würden. Gfs sollte dem Elko noch eine keramischer Kondensator beigefügt werden.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:Led an 220V.png|left|300px|230V-Schaltung]]&lt;br /&gt;
Statt eines Gleichrichters, kann auch eine Antiparallelschaltung von LEDs verwendet werden. Auch dabei werden beide Halbwellen genutzt. Die Ausfallwahrscheinlichkeit ist geringer, weil weniger Bauteile und vor allem kein Elko genutzt wird. Allerdings muss die Schaltung genau dimensioniert werden, d.h. die strombegrenzende Wirkung des Kondensators ist sehr wichtig. Daher werden zur Sicherheit einige in Serie geschaltet, um das Problem der Alterung oder Defektbildung zu minimieren. Die LEDs leuchten bei dieser Schaltungsform aber etwas dunkler als oben, weil kein konstanter Strom fliessen kann und man mit Rücksicht auf die Lebensdauer der LEDs nicht einfach die Spannung so erhöhen kann, dass der Effektivwert erreicht wird.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{warnung|Spannungen ab 60V sind lebensgefährlich!}}&lt;br /&gt;
&lt;br /&gt;
== Funktion einer LED-Lampe am Beispiel==&lt;br /&gt;
von [[Benutzer:Didi34]]&lt;br /&gt;
=== Aufbau ===&lt;br /&gt;
[[Datei:aufbau.JPG|left|300px]]&lt;br /&gt;
Ich beschreibe in diesem Beitrag den Aufbau eines 4W Philips LED-Leuchtmittels mit E-14 Sockel. Über dem LED-Chip befindet sich ein Glaskolben, der in den weißen Kunststoffsockel geklebt ist. Der Glaskolben ist aus Milchglas, dies dient als Diffusor. Um näher in die Lampe zu kommen, muss der Glaskolben abgenommen werden.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:sockel.JPG|left|300px]]&lt;br /&gt;
Nun sieht man den LED-Chip in der Mitte der Lampe. Über dem Chip befindet sich eine weitere Abdeckung, die abgenommen werden muss. &lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:chip_anschluss.JPG|left|300px]]&lt;br /&gt;
Auf dem nächsten Bild sieht man die Aluminium-Platine, worauf sich der LED-Chip befindet. Die beiden Drähte stellen die Versorgung der LED (300V Gleichspannung) dar. Die Platine besteht aus dem Basismaterial Aluminium, um eine bessere Kühlung zu erreichen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
=== Innenleben ===&lt;br /&gt;
[[Datei:platine_unten.jpg|left|300px]]&lt;br /&gt;
Im Inneren der LED-Lampe befindet sich folgende Platine:&lt;br /&gt;
&lt;br /&gt;
Man sieht nun auf der Platine eine kleine Gleichrichterschaltung. Die Anschlüsse Blau und Braun sind Neutralleiter(N) und Außenleiter(L1) unserer Energieversorgung mit 230V Wechselspannung.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:platine_oben.JPG|left|300px]]&lt;br /&gt;
&lt;br /&gt;
Die beiden Drähte Rot(+) und Schwarz(-) sind die Ausgänge der Schaltung. Sie führen eine Gleichspannung von 300V und werden direkt an der Aluminiumplatine der LED angeschlossen, da die LED für diese Spannung ausgelegt ist. Näheres zur Schaltung:&lt;br /&gt;
[http://www.mikrocontroller.net/articles/LED-Gl%C3%BChbirne#Schaltung/ Schaltung]&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== LED-Chip ====&lt;br /&gt;
[[Datei:ledchip_dunkel.jpg|left|300px]]&lt;br /&gt;
Der LED-Chip besteht aus mehreren in Serie geschalteten LEDs. Die in Serie geschalteten LEDs sind um eine warm-weiße Farbe zu erreichen teils rot und teils weiß. Die LED ist für 300V DC ausgelegt. Auf dem LED-Chip befinden sich zwei weiße und zwei rote LED-Arrays. Die gelbe Schicht der weißen LEDs ist ein Phosphor (also ein Leuchtstoff) der aus dem blauen Licht der Kristalle weißes Licht macht.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:ledchip_hell.jpg|left|300px]]&lt;br /&gt;
Eine weitere Ansicht des LED-Chips im Betrieb unter Spannung.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== Schaltung ====&lt;br /&gt;
[[Datei:netzteilschaltung.png|left|300px|Einfache Gleichrichterschaltung für LEDs]]&lt;br /&gt;
Für den Betieb an Netzspannung wird ein Kondensatornetzteil verwendet, welches aus 230V AC 300V DC macht. Die Schaltung ist mit 1A abgesichert. {{Absatz}}&lt;br /&gt;
&lt;br /&gt;
=== Messung ===&lt;br /&gt;
[[Datei:messung.bmp|left|300px|Messung]]&lt;br /&gt;
Es wurde zwischen dem roten und schwarzen Ausgang der oben genannten Schaltung gemessen. CH1 roter Anschluss CH2 schwarzer Anschluss.&lt;br /&gt;
Die rote Linie am Oszillogramm ist Die Spannung zwischen den beiden Drähten (CH1-CH2).&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
{{warnung|Spannungen ab 60V sind lebensgefährlich!}}&lt;br /&gt;
&lt;br /&gt;
== Interne Links ==&lt;br /&gt;
* [[LED]]&lt;br /&gt;
&lt;br /&gt;
== Externe Links ==&lt;br /&gt;
&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Leuchtdiode Wikipedia: LED]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kompaktleuchtstofflampe Wikipedia: Kompaktleuchtstofflampe]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Glühlampe Wikipedia: Glühbirne ]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kondensatornetzteil Wikipedia: Kondensatornetzteil]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Energiesparende Leuchtmittel Wikipedia: Energiesparende Leuchtmittel allgemein]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]]&lt;br /&gt;
[[Kategorie:Displays und Anzeigen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=LED-%22Birnen%22&amp;diff=107151</id>
		<title>LED-&quot;Birnen&quot;</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=LED-%22Birnen%22&amp;diff=107151"/>
		<updated>2024-10-30T12:51:44Z</updated>

		<summary type="html">&lt;p&gt;Heha: Text zeitgemäß: Kompaktleuchtstofflampen interessieren nur noch im Museum (als Irrweg der Beleuchtungstechnik)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Beitrag beschreibt Aufbau und Funktion von [[LED]]-basierten Leuchtmitteln (LED-Lampen), umgangssprachlich auch manchmal als &#039;&#039;&#039;&amp;quot;LED-Birnen&amp;quot;&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Anstelle von Glühlampen werden heutzutage nahezu ausschließlich LED-„Birnen“ benutzt. Im Vergleich zu den Anfang des Jahrtausends eingesetzten Kompaktleuchtstofflampen (Quecksilberdampf-Niederdrucklampe) erreichen sie ein noch höheres Energiesparpotenzial ohne Problemabfall zu sein. Dabei erreichen sie die volle Helligkeit sofort nach dem Einschalten, genauso wie Metalldraht-Glühlampen.&lt;br /&gt;
Dabei dominieren Bauformen, die auf leichte Fertigung optimiert sind (plane Leiterplatte und Lichtstreukappe). Für repräsentativen Einsatz (sichtbare Birne) gibt es sogenannte Filamentlampen, deren LED-Nacktchips dicht an dicht auf ein Keramiksubstrat gebondet sind.&lt;br /&gt;
&lt;br /&gt;
== Vergleich mit anderen Leuchtmitteln ==&lt;br /&gt;
Auf den Packungen der Hersteller sind sowohl bei Kompaktleuchtstofflampen, als auch bei LED-Birnen sehr optimistische Werte für die Lebensdauer und die Helligkeit angegeben. &lt;br /&gt;
&lt;br /&gt;
Seit 1. September 2013 ist die [http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=OJ:L:2012:342:0001:0022:DE:PDF DIM II – EU 1194/2012 EU Verordnung] in Kraft, die gewisse Mindestanforderungen an die angegeben Werte gibt. Die Anforderungen werden schrittweise in 3 Stufen verschärft: &lt;br /&gt;
 - Stufe 1: 1. September 2013, &lt;br /&gt;
 - Stufe 2: 1. September 2014&lt;br /&gt;
 - Stufe 3: 1. September 2016&lt;br /&gt;
&lt;br /&gt;
Die Einhaltung wird durch Marktaufsichtsbehörden zu kontrollieren; Man kann sich nur selber überlegen ob eher die Marken oder Noname-Hersteller sich daran halten werden.&lt;br /&gt;
&lt;br /&gt;
=== Lebensdauervergleich===&lt;br /&gt;
Kompaktleuchtstofflampen werden gerne mit bis zu 10.000h - LED-Lampen sogar mit bis zu 50.000h angegeben. &lt;br /&gt;
&lt;br /&gt;
Für LED Lampen fordert die DIM II eine maximale Frühausfallrate von 5% nach 1000h und eine maximale Ausfallrate von 10% nach 6000h. (ab Stufe 1)&lt;br /&gt;
&lt;br /&gt;
Kompaktleuchtstofflampen dürfen maximale 5% Frühausfallrate nach 500h haben und nach 6000h dürfen maximal 50% ausgefallen sein. (Stufe 1; Stufe 3: 70%)&lt;br /&gt;
&lt;br /&gt;
Ein weiterer Punkt ist die alterungsbedingte Lichtstromabnahme: Nach der Richtlinie müssen für Kompaktleuchtstofflampen nach 2000h noch 80% (Stufe 2) bzw 83% nach 2000h und 70% nach 6000h (Stufe 3)&lt;br /&gt;
&lt;br /&gt;
LEDs müssen nach 6000h noch mindestens 80% Lichtstrom haben. (Stufe 2)&lt;br /&gt;
&lt;br /&gt;
Zum Vergleich: 50% der Glühlampen müssten &amp;gt;1000h (Stufe 1), 2000h (Stufe 2) bzw. für Kleinspannungslampen ab Stufe 3 4000h leben.&lt;br /&gt;
Dabei müssen sie bei 3/4 der obigen Lebensdauerforderung noch mindestens 80% der Helligkeit haben. Frühausfallraten wären &amp;lt;5% bei 200h.&lt;br /&gt;
Wären, da bekannterweise die Glühlampen die Anforderungen &lt;br /&gt;
&lt;br /&gt;
=== Leistungsvergleich===&lt;br /&gt;
&lt;br /&gt;
Nach der DIM II ist es vorgeschrieben, dass die Lampen oder die Verpackung den Lichtstromwert in Lumen ausweisen. Damit werden die Leuchtmittel direkt vergleichbar in Punkto Helligkeit. Zusätzlich dürfen die Äquivalenzwerte angegeben werden, wobei die Umrechnung vom Lichtstrom vorgegeben ist. In der Regel benötigen hier dann LED-Lampen &amp;quot;mehr&amp;quot; Lumen als herkömmliche Glühlampen um die selbe Äquivalenzleistung zu haben.&lt;br /&gt;
&lt;br /&gt;
Beispiel: eine GU20 40D 35W Halogen hat ca. 140lm. Die selbe Lichtmenge reicht nur für das Labeln von 25W (Äquivalent). 35W würden 230lm brauchen. (Beispiel von [http://www.lighting.philips.com/pwc_li/main/led/assets/final%20dim2-philips_presentation.pdf hier, Seite 8])&lt;br /&gt;
&lt;br /&gt;
Im direkten Vergleich ist der Lichstrom sinnvoller, da hier die Augenempfindlichkeit schon berücksichtigt ist.&lt;br /&gt;
&lt;br /&gt;
=== Farbwiedergabe- und Lichtfarbenvergleich ===&lt;br /&gt;
&lt;br /&gt;
Auch Anforderungen an die Farbwiedergabe und an die Lichtfarbe werden in der DIM 2 formuliert:&lt;br /&gt;
&lt;br /&gt;
==== Farbwiedergabe ====&lt;br /&gt;
&lt;br /&gt;
Glühlampen haben per Definition einen Farbwiedergabewert Ra=100.&lt;br /&gt;
Für LED und Kompaktleuchtstofflampen sind die Mindestanforderungen Ra&amp;gt;85 (bzw. Ra&amp;gt;65 für Aussen- oder Industrieanwendungen). Gute Lampen gibt es aber auch mit Ra&amp;gt;90.&lt;br /&gt;
&lt;br /&gt;
==== Lichtfarbe ====&lt;br /&gt;
&lt;br /&gt;
Auch die [https://de.wikipedia.org/wiki/Farbtemperatur Lichtfarbe] muss angegeben werden. Hier ist die Einheit Kelvin und je kleiner der Wert desto &amp;quot;wärmer&amp;quot; das Licht. &lt;br /&gt;
&lt;br /&gt;
Normalerweise bekommt man hier nur &amp;quot;warmweiss&amp;quot; oder 3000K. &lt;br /&gt;
Damit sollten die Zeiten vorbei sein wo man vom Chinamann eine 8000K LED-Lampe als warmweiss verkauft bekommt und dann sich über das blaue Licht wundert.&lt;br /&gt;
&lt;br /&gt;
Bei LEDs darf maximale Farbabweichung darf hier nicht mehr als 6 [http://fastvoice.net/2013/09/18/farbkonsistenz-macadam-sdcm-wie-unterschiedlich-leuchten-leds/ Schwellwerteinheiten] sein. Der Wert sollte angegeben sein, gut Lampen erreichen hier weniger als 3 Schwellwerteinheiten.&lt;br /&gt;
&lt;br /&gt;
=== Vor- und Nachteile der LED-Lampen ===&lt;br /&gt;
&lt;br /&gt;
LED-Lampen haben einige Vorteile gegenüber den Kompaktleuchtstofflampen und vor allem gegenüber den Glühfadenlampen: &lt;br /&gt;
&lt;br /&gt;
* Die Energieeffizienz ist in der Regel höher als bei Kompaktleuchststofflampen.&lt;br /&gt;
* Die Kosteneffizienz ist bei einfachen Ausführungen ähnlich gut.&lt;br /&gt;
* Die Lichtfarbe bzw. Farbtemperatur der LED-Lampe ist flexibler und kann auf Wunsch der Glühfadenlampenfarbe besser angepasst werden. Dazu gibt es kaltweiße, warmweiße und auch farbige Ausführungen.&lt;br /&gt;
* Sie enthalten keine leicht freisetzbaren Giftstoffe, wie Quecksilber.&lt;br /&gt;
* Erreichen bereits nach wenigen Millisekunden nach dem Einschalten die volle Leuchtkraft.&lt;br /&gt;
* Können eine sehr lange Lebensdauer haben (meist &amp;gt;= 30000 Stunden).&lt;br /&gt;
* Sind nach Lebensende &amp;quot;nur&amp;quot; als Elektroschrott zu entsorgen (aber kein Sondermüll wie Energiesparlampen).&lt;br /&gt;
&lt;br /&gt;
Jedoch gibt es auch einige Nachteile:&lt;br /&gt;
&lt;br /&gt;
* bei birnenähnlichen Designs und höheren Leistungen höherer Preis &lt;br /&gt;
* Ähnlich wie bei ESLs befinden sich sehr viel billige Produkte am Markt, bei denen die Elektronik rasch kaputt geht.&lt;br /&gt;
* schlechterer Farbwiedergabeindex Ra=80-95 (Glühbirne als Referenz Ra=100)&lt;br /&gt;
* Bei Billigprodukten oftmals starke EMV-Probleme (Störungen im Kurzwellenbereich)&lt;br /&gt;
&lt;br /&gt;
=== Kostenvergleich von Energiesparlampen ===&lt;br /&gt;
[[Datei:Kostenvergleich-led-esl-birnen.jpg|left|300px|Kostenvergleich]]&lt;br /&gt;
&lt;br /&gt;
Die Grafik zeigt 3 Fälle von Kostenrechnungen für konventionelle Glühlampen, Kompaktleuchtstofflampe (als ESL bezeichnet) und LED-Lampen.&lt;br /&gt;
&lt;br /&gt;
Berechnet werden jeweils die beiden Extremfälle einer Lampe mit niedrigem Preis und hoher Lebensdauer (günstiger Fall) und einer &amp;quot;Montagslampe&amp;quot;, die schnell kaputt geht, trotz hohen Preises. Daraus wird ein wahrscheinlichster Mittelwert (geometrische Mitte) - einmal für geringe und einmal für starke Nutzung gebildet.&lt;br /&gt;
&lt;br /&gt;
Aufgeführt ist auch die Unterscheidung der privaten und geschäftlichen Nutzung, bei der die Beschaffungs- und Wartungszeiten viel stärker zu Buche schlagen, da ein Angestellter bezahlt werden muss. Hier zeigt sich der Vorteil der langen Nutzungsdauer der LED-Lampen besonders deutlich.&lt;br /&gt;
&lt;br /&gt;
Es zeigt sich auch, dass Privatnutzer durchaus noch einen geringen Sparvorteil haben können, wenn sie auf LED-Lampen umrüsten.&lt;br /&gt;
&lt;br /&gt;
Excel für eigene Berechnungen: [http://www.mikrocontroller.net/wikifiles/0/09/Lampenrechner.xls Lampenvergleichsrechner]&lt;br /&gt;
&lt;br /&gt;
Externer Link auf Excel:&lt;br /&gt;
[http://shop.bioledex.de/files/BIOLEDEX-LED-Stromkosten-Rechner.xls Stromkostenrechner]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
== Bauformen ==&lt;br /&gt;
=== Hochvolt-LED-Lampen===&lt;br /&gt;
&lt;br /&gt;
=== Niederspannungs-LED-Lampen===&lt;br /&gt;
==== Halogenersatzlampen ====&lt;br /&gt;
Üblicherweise werden Niederspannungs-LED-Lampen an einem dezentralen Transformator betrieben, wie z.B mit 24V. Mit einer geeigneten Vorschaltung zur Spannungsherabsetzung im Lampengehäuse, sind sie auch indirekt an 230V-Netzen benutzbar.&lt;br /&gt;
&lt;br /&gt;
==== Betrieb von Standard-LEDs an 230V ====&lt;br /&gt;
[[Datei:led_230v.png|left|300px|230V-Vorschaltung]]&lt;br /&gt;
Mit der nebenstehenden Schaltung besteht die Möglichkeit, eine normale LED direkt an 230V anzuschließen. Diese Schaltung funktioniert so, dass die Spannung mittels Z-Dioden begrenzt wird; in diesem Fall auf 30V. Diese sind deshalb wichtig, weil die Gleichrichtdioden für hohen Frequenzanteile als Kapazität wirken und der Elko diese nicht aufnehmen kann, wodurch sie direkt auf die LEDs wirken würden. Gfs sollte dem Elko noch eine keramischer Kondensator beigefügt werden.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:Led an 220V.png|left|300px|230V-Schaltung]]&lt;br /&gt;
Statt eines Gleichrichters, kann auch eine Antiparallelschaltung von LEDs verwendet werden. Auch dabei werden beide Halbwellen genutzt. Die Ausfallwahrscheinlichkeit ist geringer, weil weniger Bauteile und vor allem kein Elko genutzt wird. Allerdings muss die Schaltung genau dimensioniert werden, d.h. die strombegrenzende Wirkung des Kondensators ist sehr wichtig. Daher werden zur Sicherheit einige in Serie geschaltet, um das Problem der Alterung oder Defektbildung zu minimieren. Die LEDs leuchten bei dieser Schaltungsform aber etwas dunkler als oben, weil kein konstanter Strom fliessen kann und man mit Rücksicht auf die Lebensdauer der LEDs nicht einfach die Spannung so erhöhen kann, dass der Effektivwert erreicht wird.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{warnung|Spannungen ab 60V sind lebensgefährlich!}}&lt;br /&gt;
&lt;br /&gt;
== Funktion einer LED-Lampe am Beispiel==&lt;br /&gt;
von [[Benutzer:Didi34]]&lt;br /&gt;
=== Aufbau ===&lt;br /&gt;
[[Datei:aufbau.JPG|left|300px]]&lt;br /&gt;
Ich beschreibe in diesem Beitrag den Aufbau eines 4W Philips LED-Leuchtmittels mit E-14 Sockel. Über dem LED-Chip befindet sich ein Glaskolben, der in den weißen Kunststoffsockel geklebt ist. Der Glaskolben ist aus Milchglas, dies dient als Diffusor. Um näher in die Lampe zu kommen, muss der Glaskolben abgenommen werden.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:sockel.JPG|left|300px]]&lt;br /&gt;
Nun sieht man den LED-Chip in der Mitte der Lampe. Über dem Chip befindet sich eine weitere Abdeckung, die abgenommen werden muss. &lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:chip_anschluss.JPG|left|300px]]&lt;br /&gt;
Auf dem nächsten Bild sieht man die Aluminium-Platine, worauf sich der LED-Chip befindet. Die beiden Drähte stellen die Versorgung der LED (300V Gleichspannung) dar. Die Platine besteht aus dem Basismaterial Aluminium, um eine bessere Kühlung zu erreichen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
=== Innenleben ===&lt;br /&gt;
[[Datei:platine_unten.jpg|left|300px]]&lt;br /&gt;
Im Inneren der LED-Lampe befindet sich folgende Platine:&lt;br /&gt;
&lt;br /&gt;
Man sieht nun auf der Platine eine kleine Gleichrichterschaltung. Die Anschlüsse Blau und Braun sind Neutralleiter(N) und Außenleiter(L1) unserer Energieversorgung mit 230V Wechselspannung.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:platine_oben.JPG|left|300px]]&lt;br /&gt;
&lt;br /&gt;
Die beiden Drähte Rot(+) und Schwarz(-) sind die Ausgänge der Schaltung. Sie führen eine Gleichspannung von 300V und werden direkt an der Aluminiumplatine der LED angeschlossen, da die LED für diese Spannung ausgelegt ist. Näheres zur Schaltung:&lt;br /&gt;
[http://www.mikrocontroller.net/articles/LED-Gl%C3%BChbirne#Schaltung/ Schaltung]&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== LED-Chip ====&lt;br /&gt;
[[Datei:ledchip_dunkel.jpg|left|300px]]&lt;br /&gt;
Der LED-Chip besteht aus mehreren in Serie geschalteten LEDs. Die in Serie geschalteten LEDs sind um eine warm-weiße Farbe zu erreichen teils rot und teils weiß. Die LED ist für 300V DC ausgelegt. Auf dem LED-Chip befinden sich zwei weiße und zwei rote LED-Arrays. Die gelbe Schicht der weißen LEDs ist ein Phosphor (also ein Leuchtstoff) der aus dem blauen Licht der Kristalle weißes Licht macht.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
[[Datei:ledchip_hell.jpg|left|300px]]&lt;br /&gt;
Eine weitere Ansicht des LED-Chips im Betrieb unter Spannung.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== Schaltung ====&lt;br /&gt;
[[Datei:netzteilschaltung.png|left|300px|Einfache Gleichrichterschaltung für LEDs]]&lt;br /&gt;
Für den Betieb an Netzspannung wird ein Kondensatornetzteil verwendet, welches aus 230V AC 300V DC macht. Die Schaltung ist mit 1A abgesichert. {{Absatz}}&lt;br /&gt;
&lt;br /&gt;
=== Messung ===&lt;br /&gt;
[[Datei:messung.bmp|left|300px|Messung]]&lt;br /&gt;
Es wurde zwischen dem roten und schwarzen Ausgang der oben genannten Schaltung gemessen. CH1 roter Anschluss CH2 schwarzer Anschluss.&lt;br /&gt;
Die rote Linie am Oszillogramm ist Die Spannung zwischen den beiden Drähten (CH1-CH2).&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
{{warnung|Spannungen ab 60V sind lebensgefährlich!}}&lt;br /&gt;
&lt;br /&gt;
== Interne Links ==&lt;br /&gt;
* [[LED]]&lt;br /&gt;
&lt;br /&gt;
== Externe Links ==&lt;br /&gt;
&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Leuchtdiode Wikipedia: LED]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kompaktleuchtstofflampe Wikipedia: Kompaktleuchtstofflampe]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Glühlampe Wikipedia: Glühbirne ]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kondensatornetzteil Wikipedia: Kondensatornetzteil]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Energiesparende Leuchtmittel Wikipedia: Energiesparende Leuchtmittel allgemein]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]]&lt;br /&gt;
[[Kategorie:Displays und Anzeigen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107033</id>
		<title>FET</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107033"/>
		<updated>2024-07-24T09:31:12Z</updated>

		<summary type="html">&lt;p&gt;Heha: Erklärung Warum und Woher&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel versteht sich als Unterpunkt zum Artikel [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein FET (engl. &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor) ist ein  Feldeffekttransistor. Der FET ist ein Bauelement, das im Gegensatz zum Bipolartransistor (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT) mit Spannung und nicht mit Strom gesteuert wird. Weil keine P-N-Sperrschichten am Stromfluss beteiligt sind, heißen diese auch Unipolartransistoren. Unterschieden werden&lt;br /&gt;
&lt;br /&gt;
* MOSFET = engl. &#039;&#039;&#039;M&#039;&#039;&#039;etall &#039;&#039;&#039;O&#039;&#039;&#039;xide &#039;&#039;&#039;S&#039;&#039;&#039;emiconductor &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor; Metalloxidschicht-FET, größte Teilgruppe der FETs mit isoliertem Gate.&lt;br /&gt;
&lt;br /&gt;
* JFET = engl. &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, Übergangszonen-FET, der steuerbare Kanal wird durch einen PN-Übergang wie in einer Diode gebildet. &amp;lt;small&amp;gt;(Diese Diode lässt sich nachmessen und unterscheidet JFET von (selbtleitenden) MOSFET, auch wenn sie schaltungstechnisch keine Rolle spielt. Wichtig: JFETs gibt es &#039;&#039;nur&#039;&#039; selbstleitend!)&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die drei Anschlüsse eines FETs werden &#039;&#039;Gate&#039;&#039;, &#039;&#039;Drain&#039;&#039; und &#039;&#039;Source&#039;&#039; genannt. Unter Umständen ist ein vierter Anschluß vorhanden, der &#039;&#039;Bulk&#039;&#039; genannt wird. Normalerweise ist Bulk intern mit Source verbunden. &amp;lt;small&amp;gt;Wenn dies nicht der Fall ist, muss diese Verbindung durch den Designer in der Schaltung hergestellt werden. Nicht gesichtet für Leistungstransistoren.&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== FET-Typen ==&lt;br /&gt;
&lt;br /&gt;
FETs werden hauptsächlich unterschieden in N-Kanal und P-Kanal, sowie &amp;quot;selbst sperrend = Anreicherungstyp&amp;quot; (engl. enhancement type) und &amp;quot;selbst leitend = Verarmungstyp&amp;quot; (engl. depletion type). Beim selbstleitenden FET ist der Transistor bei 0V Gate-Source Spannung maximal leitend (durchgesteuert) und wird durch Anlegen einer Spannung ans Gate gesperrt. Beim selbstsperrenden FET (größte Gruppe) ist der Transistor bei 0V Gate-Source Spannung gesperrt und wird durch Anlegen einer Spannung ans Gate leitend. Ist die Linie zwischen Drain und Source durchgezogen handelt es sich um einen selbstleitenden, bei einer gestrichelten Linie um einen selbstsperrenden FET. JFETs gibt es nur als Verarmungstyp.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Artikel wird nur der &#039;&#039;&#039;selbstsperrende&#039;&#039;&#039; MOSFET betrachtet, weil nur dieser einen althergebrachten Bipolartransistor „ersetzen“ kann.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Typen von Feldeffekttransistoren&amp;lt;br/&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Typ &lt;br /&gt;
! N-Kanal &lt;br /&gt;
! P-Kanal&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| JFET&amp;lt;br/&amp;gt;SFET&amp;lt;br/&amp;gt;(selbst leitend) || [[bild:JFET-N.png|center]]&lt;br /&gt;
* drittgrößte Gruppe&lt;br /&gt;
* bislang nur für kleine Leistungen verfügbar&lt;br /&gt;
* JFETs mit hoher Leistung sind im Kommen&lt;br /&gt;
* Eingangsstufen von OPVs&lt;br /&gt;
* Eingangsstufen von HF-Verstärkern bis in den GHz-Bereich&lt;br /&gt;
* als einfache [[Konstantstromquelle]] geeignet&lt;br /&gt;
| [[bild:JFET-P.png|center]]&lt;br /&gt;
* selten&lt;br /&gt;
* ersatzhalber für „fehlenden“ P-Kanal-Verarmungs-MOSFET&lt;br /&gt;
* Ältester Vertreter der Unipolartransistoren&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Anreicherungstyp&amp;lt;br/&amp;gt;(selbst sperrend)&amp;lt;br/&amp;gt;&#039;&#039;Enhancement Mode&#039;&#039; || [[bild:MOS-EN.png|center]]&lt;br /&gt;
* Mit Abstand größte Gruppe; wenn nicht besonders erwähnt ist &#039;&#039;dieser&#039;&#039; gemeint&lt;br /&gt;
* Eingangsstufen von HF-Verstärkern bis in den GHz-Bereich&lt;br /&gt;
** Typen mit zwei Gates zum Regeln und Mischen üblich&lt;br /&gt;
* Auch als Doppel-MOSFET oder zusammen mit P-Kanal-MOSFET in einem Gehäuse gängig&lt;br /&gt;
| [[bild:MOS-EP.png|center]]&lt;br /&gt;
* zweitgrößte Gruppe&lt;br /&gt;
* bei gleicher Geometrie etwas schlechter als ein N-Kanal-Typ, da „Löcherleitung“ (aus „Defektelektronen“ = gedachte positive Ladungsträger) vorliegt&lt;br /&gt;
* Auch als Doppel-MOSFET oder zusammen mit N-Kanal-MOSFET in einem Gehäuse gängig&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Verarmungstyp&amp;lt;br/&amp;gt;(selbst leitend)&amp;lt;br/&amp;gt;&#039;&#039;Depletion Mode&#039;&#039; || [[bild:MOS-DN.png|center]]&lt;br /&gt;
* selten aber käuflich&lt;br /&gt;
* Sinnvolle Anwendungen ergeben sich vor allem in Reihenschaltungen&lt;br /&gt;
| [[bild:MOS-DP.png|center]]&lt;br /&gt;
* Nicht in freier Wildbahn gesichtet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Vorteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Niedrigere Verluste als bei Bipolartransistoren.&lt;br /&gt;
* Sehr schnelles Schalten möglich, daher für sehr hohe Frequenzen geeignet (keine Speicherzeit wie beim BJT).&lt;br /&gt;
* Einfaches Parallelschalten im Schaltbetrieb, da Unterschiede im &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; durch den positiven Temperaturkoeffizienten ausgeglichen werden.&lt;br /&gt;
* Leistungslose Ansteuerung im statischen Fall, jedoch hohe Umladeverluste am Gate!&lt;br /&gt;
* oft preiswerter als vergleichbare Bipolartransistoren (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT)&lt;br /&gt;
* Relativ unempfindlich gegen Überspannung zwischen Drain und Source. Bei Überschreitung der Maximalspannung zwischen Drain und Source findet ein sogenannter &amp;quot;Durchbruch&amp;quot; statt. Dies ist vergleichbar mit dem Zener-Effekt. Ist die Energiemenge begrenzt, ist dieser Durchbruch reversibel und der FET wird NICHT zerstört. Beim Bipolartransistor ist die dafür zulässige Energiemenge viel kleiner, weil die den Strom durchquerende Fläche zu Hotspots neigt.&lt;br /&gt;
* Möglichkeit eines Stromfühler-Anschlusses: Durch geeignete Geometrie kann der Source-Anschluss aufgeteilt werden, etwa im Verhältnis 1:9 bis 1:99. Der Drainstrom teilt sich kontrolliert auf, auch wenn am „kleineren“ Source-Anschluss ein Messwiderstand zur Spannungs-Strom-Wandlung angeschlossen ist. Leider sind solche „Sense-MOSFETs“ selten einzeln und faktisch nie als P-Kanal-Typen verfügbar. In integrierten (Schaltnetzteil-)Schaltungen sind sie Standard.&lt;br /&gt;
&lt;br /&gt;
=== Nachteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Nur bedingt für hohe Spannungen [[Transistor#Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein ? |geeignet]], die ON-Verluste sind ab ca. 250V höher als bei einem [[IGBT]]. &lt;br /&gt;
* Parasitäre Diode parallel zur Drain-Source Strecke ist immer enthalten, das (Ab-)Schaltverhalten dieser Dioden ist meist schlechter als separate Dioden, was häufig zu unerwünschten Schwingungen führt.&lt;br /&gt;
* Empfindlicher gegen ESD am Gate als BJT&lt;br /&gt;
* Positiver Temperaturkoeffizient (TK), der &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; ist stark temperaturabhängig und steigt von 25°C (Datenblattangabe) auf 150°C ungefähr um den Faktor 2. Dadurch steigen auch die Verluste und damit die Erwärmung des Bauteiles.&lt;br /&gt;
&lt;br /&gt;
=== Erklärung der wichtigsten Datenblattwerte ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:7em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | Beispiel&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Drain Source Voltage &amp;lt;br&amp;gt;(Breakdown) || V(BR)_DSS &amp;lt;br&amp;gt; V_DS || 75 V || Maximale Spannungsfestigkeit des Bauteiles zwischen Drain und Source&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Drain Current  || I_D(on)   || 55 A @ 125 °C  || Maximaler Dauerstrom bei 125°C Gehäusetemperatur &lt;br /&gt;
|-&lt;br /&gt;
| Pulsed Drain Current || ID_pulse &amp;lt;br&amp;gt; I_CD(on) || 240 A || Maximaler Pulsstrom (Achtung die zulässige Zeitdauer des Pulses kann nur über die maximale Junctiontemperatur ermittelt werden)&lt;br /&gt;
|-&lt;br /&gt;
| Gate Charge || Q&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; ||  320 nC || Gate-Ladung in Coloumb oder Amperesekunden, die zum Ein- oder Ausschalten des MOSFET erforderlich ist. Dies berücksichtigt die Nichtlinearität der Kapazität und (richtig?) den Miller-Effekt. Mit dieser Angabe wird der für das Schalten von Leistungs-MOSFET benötigte Gatestrom für eine bestimmte Schaltzeit ermittelt. Dieser liegt üblicherweise im einstelligen Ampere-Bereich. Moderne Typen mit extra geringer Gate-Ladung können so schneller schalten.&lt;br /&gt;
|-&lt;br /&gt;
| Repetetive Avalanche Energy || t_sc ||  280 mJ || Maximale Energie, welche beim Avalanche Durchbruch bei Überschreiten der maximalen Drain-Source Spannung im MOSFET bei z.&amp;amp;nbsp;B. 1% Puls/Pausen Verhältnis regelmäßig auftreten darf, ohne den FET zu schädigen&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_ON&amp;lt;/sub&amp;gt; ||  0,01 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;25°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_on&amp;lt;/sub&amp;gt; ||  0,021 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;175°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Thermal Resistance&amp;lt;br&amp;gt;(junction-case) ||  R&amp;lt;sub&amp;gt;th_JC&amp;lt;/sub&amp;gt; ||  0,8 K/W || Thermischer Widerstand im Transistor vom der „Sperrschicht“ (junction eines Bipolartransistors, ja altmodischer Index, hier besser „Kanal“) bis zur Rückseite oder bestmöglichen Kühlmöglichkeit des Transistorgehäuses (case)&lt;br /&gt;
|-&lt;br /&gt;
| Gate-Source&amp;lt;br&amp;gt;Threshold Voltage ||  V_GS(th) || 2,0-4,5 V || Gatespannung, ab welcher der Transistor minimal leitend wird (I_D typisch 100-200µA), große Toleranz, typisch 1:2 zwischen Minimum und Maximum&lt;br /&gt;
|-&lt;br /&gt;
| Turn-on Delay ||  t_d(on) || 40 ns ||  Verzögerung zwischen dem Einschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Rise Time ||  t_r || 200 ns ||  Anstiegszeit des Transistorstromes am Drain&lt;br /&gt;
|-&lt;br /&gt;
| Turn-off Delay || t_d(off) || 120 ns ||  Verzögerung zwischen Abschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Fall Time ||  t_f  || 60 ns || Abfallzeit des Transistorstromes am Drain  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die oben genannten Zeiten gelten ausschließlich unter den angegebenen Messbedingungen (Gatewiderstand, Treiberspannung, sowie einer &#039;&#039;&#039;FET-Teperatur von 25°C!&#039;&#039;&#039;) und müssen für die eigene Anwendung ggf. neu berechnet werden. Meist wird man sie eher messen, weil die Rechung zu aufwändig und bisweilen unmöglich ist. &lt;br /&gt;
&lt;br /&gt;
==== Gate-Source Threshold Voltage ====&lt;br /&gt;
Gerade bei der &#039;&#039;&#039;Gate-Source Threshold Voltage &amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt;&#039;&#039;&#039; gibt es hier immer wieder Verwirrung. Sie gibt an, ab welcher Spannung der MOSFET anfängt, minimal leitfähig zu werden. Diese Spannung ist technologisch bedingt auch heute noch einer starken Toleranz unterworfen, typischerweise hat der Bereich  bei dem der FET zu leiten beginnt eine Spreizung von etwa 1:2 zwischen Minimum und Maximum. Welche Spannung man nun wirklich anlegen muss, um den gewünschten &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt; zu erreichen kann der Tabelle im jeweiligen Datenblatt entnommen werden. Dabei unbedingt die angegebene Gatespannung beachten, nur dieser Wert ist garantiert!.&lt;br /&gt;
&lt;br /&gt;
[[bild: IRLZ34N_R_DS_ON.png | thumb | 800px | R_DS_ON im Datenblatt des IRLZ34N]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Die Kurvenschar von &amp;lt;math&amp;gt;I_D&amp;lt;/math&amp;gt; über &amp;lt;math&amp;gt;U_{DS}&amp;lt;/math&amp;gt; in Abhängigkeit von &amp;lt;math&amp;gt;U_{GS}&amp;lt;/math&amp;gt; stellt immer nur typische Werte dar, keine garantierten Extremwerte (engl. worst case). Als Standardwerte kann man 10-15V für einen Standardtypen und ca. 3-5V für einen Logic Level MOSFET (LL-FET) ansetzen. Kleinsignal-FETs leiten schon ab ca 1V. Bei Ansteuerung mit 5V benötigt man also einen Typen, der &#039;&#039;&#039;sicher&#039;&#039;&#039; bei 5V voll durchgesteuert ist, z.B. IRLZ34N. Bei 3,3V ist er bereits nicht mehr zuverlässig nutzbar. Es gibt auch Typen mit noch geringerer Spannung für Vollaussteuerung. Wer einen BUZ11 (&amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt; 4V max.) mit 5V ansteuert riskiert ein Abfackeln des MOSFETs, denn je nach Toleranz kann er bereit ganz gut aufgesteuert sein oder auch nicht. Das soll in den nachfolgenden Diagrammen dargestellt werden. Zunächst die Transferkennlinie. Sie gibt an, wieviel Drainstrom in Abhängigkeit der Gatespannung fließen kann, wobei die Drainspannung konstant ist, hier im Beispiel 25V. Ein typischer BUZ11 (mittlere, schwarze Kurve) fängt bei 3V zu leiten an und erreicht bei 6V am Gate ca. 17A. Erwischt man nun ein kritisches Exemplar, das erst bei 4V zu leiten anfängt (rechte, rote Kurve um 1V parallel verschoben), so kann dieser bei 6V nur 8A leiten, für 17A braucht er 7V. Der günstige Fall, daß der BUZ11 schon bei 2V anfängt zu leiten sieht man in der linken, roten Kurve.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UGS_ID.png | thumb | 800px | Transferkennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Wenn man die drei Fälle (min, typ, max) mit 5V ansteuert, erhält man die Arbeitspunkte 1-3 mit einem maximal schaltbaren Drainstrom von 2,5, 8 und 17A. Diese kann man in das Ausgangskennlinienfeld übertragen. Wir nehmen max. 1V U_DS an. Es ergeben sie die gleichen Ströme. Der maximale Drainstrom weicht um -5,5/+9A vom typischen Fall ab. Erhöht man nun die Gate-Source Spannung auf 10V (Arbeitspunkt 4), schwankt der maximal schaltbare Drainstrom nur noch um -2/+2A, außerdem liegt er mit 30A deutlich höher. Der BUZ11 ist somit sicher durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UDS_ID_UGS.png | thumb | 800px | Ausgangskennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
==== Parasitäre Diode ====&lt;br /&gt;
&lt;br /&gt;
Der Schwerpunkt in der FET-Entwicklung liegt auf den geringst-möglichen &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt;,  die Diode entsteht auf Grund des Herstellungsprozesses, und wird nur nachrangig verbessert, da viele Optimierungsversuche auch einen Einfluss auf wichtige Kennwerte des FETs hatten und haben.&lt;br /&gt;
Daher muss sorgfältig geprüft werden, ob die Schaltgeschwindigkeit, die Recovery-Time und die damit verbundenen Verluste sowie die dabei erzeugte unerwünschte EMV-Abstahlung tolerierbar ist, oder nicht.&lt;br /&gt;
Hier hilft es oft eine optimierte Diode / Schottky-Diode zum FET parallel zu schalten. Ganz ausblenden läßt sich die parasitäre Diode jedoch nicht, jedoch kann man den Anteil des Stromes beeinflussen, der über die intere Diode fliest.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039; Parasitäre Diode des FETs  &#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Current  ||  I_S || 75A || Maximaler Dauerstrom der parasitären Diode, meist identisch zum maximalen Dauerstrom des MOSFETs&lt;br /&gt;
|-&lt;br /&gt;
| Forward Voltage ||  V_SD || 1,3V || Spannungsfall an der parasitären Diode &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Time ||  t_rr || 120ns || Zeit, welche die Elektronen brauchen um aus der leitenden Diode vollständig abzufließen. Während dieser Zeit fließt der Strom in &#039;&#039;&#039;Rückwärtsrichtung&#039;&#039;&#039; durch die Diode und erzeugt relativ viel Verlustleistung. &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Charge ||  Q_rr || 60nC || Ladungsmenge, die während t_rr rückwärts durch die Diode fließt.  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Haupttypen und Gatespannungslevel ==&lt;br /&gt;
&lt;br /&gt;
===Unterschied N-Kanal / P-Kanal FET===&lt;br /&gt;
&lt;br /&gt;
Im Schaltsymbol werden die MOSFET-Typen meist durch die Pfeilrichtung in der Mitte des Symbols (eigentlich &amp;quot;Bulk&amp;quot;) vom oder zum Gate unterschieden.  Zeigt der Pfeil zum Gate hin, handelt es sich um einen N-Kanal-FET, zeigt der Pfeil vom Gate weg um einen P-Kanal FET.&lt;br /&gt;
&lt;br /&gt;
Der große Vorteil des N-Kanal FETs (Elektronenleitung) ist, daß er immer niederohmiger ist, als ein gleich großer P-Kanal FET (Löcherleitung). Daher sind P-Kanal Typen bei vergleichbaren Werten auch immer größer = teuerer da weniger Chips auf einem Wafer Platz haben.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET muss die Gatespannung positiv gegenüber Source sein. Dabei wird der FET dann leitend, wenn die sogenannte &amp;quot;threshold voltage&amp;quot; (Schwellenspannung) erreicht wird. Eine typische Anwendung ist z.&amp;amp;nbsp;B. ein &#039;&#039;&#039;Low-Side Schalter&#039;&#039;&#039;: Source an GND, Drain an die Last, Ansteuerung des N-Kanal FETs mit 12V gleichbedeutend mit 12V ÜBER den Source = GND Potential.&lt;br /&gt;
 &lt;br /&gt;
Beim P-Kanal FET als HS-Schalter muss die Gatespannung negativer=niedriger als das Sourcepotential sein.Beispiel.&lt;br /&gt;
Beispiel:  &lt;br /&gt;
Lastspannung = 400V d.h. Source an 400V, Last zwischen Drain und GND, Ansteuerung des P-Kanal FETs mit 388V, also 12V UNTER dem Sourcepotential.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET als HS-Schalter muss die Gatespannung positver=höher als das Sourcepotential sein.&lt;br /&gt;
Beispiel:&lt;br /&gt;
Lastspannung = 400V d.h. Drain an 400V, die Last zwischen Source und GND, Ansteuerung des N-Kanal FETs mit 412V, also 12V ÜBER dem Sourcepotential.&lt;br /&gt;
In diesem Fall ist aber eine zusätzliche Spannungsquelle erforderlich, denn der FET wird mit einer Spannung über der Lastspannung eingeschaltet. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weblinks&#039;&#039;&#039;&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-1-of-2/?utm_source=eetimes&amp;amp;utm_medium=networksearch A primer on high-side FET load switches (Part 1 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products, Micrel, Inc., 5/3/2007 4:14 PM EDT, www.eetimes.com&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-2-of-2/ A primer on high-side FET load switches (Part 2 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products. Micrel, Inc., 5/7/2007 1:36 PM EDT, www.eetimes.com&lt;br /&gt;
* [http://www.vishay.com/docs/70611/70611.pdf AN804 P-Channel MOSFETs, the Best Choice for High-Side Switching (PDF)] von Vishay Siliconix&lt;br /&gt;
&lt;br /&gt;
===Unterschied Logic-Level / „Normal“-Level===&lt;br /&gt;
&lt;br /&gt;
Den meisten FETs ist gemein, daß sie mit einer Spannung von 10..15 V angesteuert werden müssen, um den minimalen Einschaltwiderstand zu erreichen. Diese FETs lassen sich nicht ohne weiteres mit einem CMOS-Pegel von 5V ansteuern. Es gibt jedoch für diesen Anwendungsfall sogenannte „Logic Level“ (LL) FETs, die schon bei einer Gatespannung von etwa 4,5V voll durchgesteuert sind. Einige Kleinsignal-FETs sind schon ab ca. 1,2V voll durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
Hochvolt-MOSFETs für Schaltnetzteil-Anwendungen gibt es &#039;&#039;faktisch nie&#039;&#039; als Logic-Level-Typen, weswegen Ansteuerschaltkreise wie UC3842 eine Unterspannungsabschaltung bei 10 V haben. Um Steuerleistung zu sparen ist hier die (niedrige) Gate-Ladung (gate charge) der wichtigere Kennwert.&lt;br /&gt;
&lt;br /&gt;
Die Schaltschwelle ergibt sich aus Dotierung und Chipgeometrie. Diese muss für Logic-Level-Typen besonders engtoleriert gehalten werden da ein „zu niedrig“ zur Selbstleitung führt (= Depletion Mode MOSFET) — ein unerwünschtes Betriebsverhalten. Das ist auch der Grund, warum die berühmt gewordene Standard-CMOS-Serie 4000 mit relativ hoher Spannung betrieben wurde, ganz zu schweigen von NMOS (12 V, dazu -5 V Substratvorspannung: EPROM 2708) oder gar PMOS (27 V): „Logic Level“, damals 5 V Logikspannung, war für MOSFET noch nicht sicher beherrschbar und hat allenfalls komplementär funktioniert.&lt;br /&gt;
&lt;br /&gt;
== Beispiel zur Bauteiledimensionierung ==&lt;br /&gt;
&lt;br /&gt;
=== Spannungsfestigkeit ===&lt;br /&gt;
&lt;br /&gt;
Die höchste vorkommende Betriebsspannung + Abschaltüberspannung soll kleiner als ca. 80% der Spannungsfestigkeit des Bauteiles sein. &lt;br /&gt;
&lt;br /&gt;
Achtung: Zwischen dem je nach Anwendungsfall erforderlichen Pufferkondensator und dem FET wird es immer eine parasitäre Induktivität geben.&lt;br /&gt;
Abhängig von Schaltgeschwindigkeit und Induktivität wird im Schaltmoment eine mehr oder weniger große Übrspannungsspitze produziert. Dieser Peak&lt;br /&gt;
addiert sich auf die aktuelle Versorgungsspannung.&lt;br /&gt;
&lt;br /&gt;
Überschlagsrechnung als Beispiel:&lt;br /&gt;
* Schaltgeschwindigkeit:  dI/dt = -100A/µs (= Abschalten von 5A innerhalb 50ns),&lt;br /&gt;
* Induktivität:   L = 1µH (~ 1 m loses, ungebündeltes Kabel)&lt;br /&gt;
* dU=-L*dI/dt = -1µH * (-100A / 1µs) = 100V&lt;br /&gt;
&lt;br /&gt;
Dies bedeutet, daß an der &amp;quot;Induktivität&amp;quot; zwischen Transistor und Kondensator - Aufgrund von Selbstinduktion im Schaltmoment - ein Überspannungspuls von ca. 100V entsteht, der auf die Betriebsspannung aufzuschlagen ist, also lieber die Leitung kürzer machen, und - sofern möglich - nicht ganz so schnell schalten.&lt;br /&gt;
&lt;br /&gt;
=== Stromtragfähigkeit ===&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt ist eine Stromtragfähigkeit bei 25°C, und meist noch bei einer höheren Temperatur z.B. 125°C, 150°C oder 175°C Kühlfahnentemperatur angegeben. Dieser Wert ist als ERSTE Entscheidungsgrundlage ausreichend, aber aus der theoretisch abführbaren Verlustleistung errechnet, und&lt;br /&gt;
* dient zum qualitativen Vergleich von Transistoren bezüglich ihres R_ds(on) und ihres Wärmewiderstands.&lt;br /&gt;
* ist für die Dimensionierung einer Schaltung nur als Richtwert zu interpretieren. &lt;br /&gt;
* ist ohne Schaltverluste genannt, und daher nur für einen Schaltbetrieb von wenigen Hz gültig. Außerdem wird ein annähernd idealer Kühlkörper unterstellt, der trotz der Verlustleistung das Gehäuse des Transistors auf der angegebenen Temperatur halten kann.&lt;br /&gt;
* entbindet einen nicht davon den Kopf einzuschalten... siehe die nachfolgenden Zeilen.&lt;br /&gt;
* Liegt der Strom für den die Schaltung entwickelt wird mit ca. 10..20% Abstand unter dem Datenblattwert von 125°C ist dieses Bauteil vermutlich verwendbar (siehe Detailberechnungen unten !).   &lt;br /&gt;
* Ist der benötigte Strom im Bereich oder größer als der zulässige bei 125°C sollte entweder ein anderer Typ eingesetzt oder mehrere FETs &#039;&#039;des gleichen Typs&#039;&#039; parallelgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
== Verlustleistung ==&lt;br /&gt;
&lt;br /&gt;
Hier wird eine Näherung für eine getaktete Anwendung betrachtet. In einem Transistor treten sowohl beim Ein- und Ausschalten, als auch während der Einschaltphase Verluste im Bauteil auf. Diese Verluste führen zu einer Bauteilerwärmung. Die dabei entstehende Temperatur darf die maximal zulässige Bauteiletemperatur nie überschreiten. Bei den ersten Projekten ist zu empfehlen eine berechnete Chiptemperatur von ca. 125°C nicht zu überschreiten. Fast alle aktuell verfügbaren FETs nennen im Datenblatt eine Temperatur von 175°C als ihre maximale Chiptemperatur.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:32em&amp;quot; &lt;br /&gt;
|+ &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Symbol&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Wert&lt;br /&gt;
|-&lt;br /&gt;
| Betriebsspannung || U&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 70 V&lt;br /&gt;
|-&lt;br /&gt;
| Nennstrom || I&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 30 A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source Widerstand bei&amp;lt;br&amp;gt;Chiptemperatur: 125°C&amp;lt;br&amp;gt; Gatespannung: 10V || R&amp;lt;sub&amp;gt;DS&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt;&amp;lt;/sub&amp;gt; || 17 mΩ&lt;br /&gt;
|-&lt;br /&gt;
| Pulsbreite || t&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt; || 150 µs&lt;br /&gt;
|-&lt;br /&gt;
| Schaltfrequenz || ƒ&amp;lt;sub&amp;gt;schalt&amp;lt;/sub&amp;gt; || 5 kHz&amp;lt;br&amp;gt;T = 200µs&lt;br /&gt;
|-&lt;br /&gt;
| Einschaltzeit (risetime) || t&amp;lt;sub&amp;gt;r&amp;lt;/sub&amp;gt; || 500 ns&lt;br /&gt;
|-&lt;br /&gt;
| Ausschaltzeit (falltime) || t&amp;lt;sub&amp;gt;ƒ&amp;lt;/sub&amp;gt; || 800 ns &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Leitend-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Während der FET bei [[PWM]]-Ansteuerung eingeschaltet ist, erzeugt er Verlustleistung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
P_\text{ON}&lt;br /&gt;
 = {I_\mathrm{N}}^2 \cdot R_\mathrm{DS_\mathrm{ON}} \cdot \frac{t_\mathrm{ON}}{T}&lt;br /&gt;
 = 30^2A^2 \cdot 17m\Omega  \cdot \frac{150\mu s}{200\mu s} = 11{,}5W&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schalt-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Vereinfachter Ansatz.&lt;br /&gt;
&lt;br /&gt;
Einschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_r}&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_r}{T} \\&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot 70V \cdot 30A \cdot \frac{500ns}{200\mu s}=1{,}3W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_f} &lt;br /&gt;
&amp;amp;=\tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_f}{T}\\&lt;br /&gt;
&amp;amp;=\tfrac14 \cdot 70V \cdot 30A \cdot \frac{800ns}{200\mu s}=2{,}1W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ und genauer kann man rechnen, wenn die Ein- Ausschaltenergie im Datenblatt angegeben ist. Aber Achtung! Die  Randbedingungen unter denen die genannte Energie ermittelt wurde, müssen genau so zutreffen.&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_f} = f_{schalt} \cdot E_{ON}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_r} = f_{schalt} \cdot E_{OFF}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Gesamtverlustleistung beträgt also in etwa 15W.&lt;br /&gt;
&lt;br /&gt;
Damit muß ein entsprechender [[Kühlkörper]] ausgelegt und die Chiptemperatur berechnet werden. z.&amp;amp;nbsp;B.:&lt;br /&gt;
* Kühlkörper mit einem R_th von 0,2K/W&lt;br /&gt;
* max. Umgebungstemperatur +60°C&lt;br /&gt;
* R_th &amp;quot;junction-case&amp;quot; des FETs 0,8K/W&lt;br /&gt;
* R_th der Wärmeleitfolie zwischen FET und Kühlkörper ca. 2,0K/W&lt;br /&gt;
* R_th gesamt: 3,0K/W &amp;lt;br&amp;gt;&lt;br /&gt;
* Bei einer Verlustleistung von 18W und einer Umgebungstemperatur von 60°C hat der Chip eine Temperatur von ca. 18W * 3,0K/W +60°C = 114°C. ==&amp;gt; o.k.!&lt;br /&gt;
&lt;br /&gt;
Unter Berücksichtigung der Tatsache, daß hier viele Vereinfachungen vorgenommen, und die Art der Last nicht beachtet wurde ist es sinnvoll, einen gewissen Sicherheitsabstand zu den zulässigen Maximalwerten einzuhalten. Daher ist es empfehlenswert, die Chiptemperatur auf ca. 125°C zu beschränken. &lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist hier die parasitäre Diode im FET nicht berücksichtigt.&lt;br /&gt;
Wenn während der &amp;quot;off&amp;quot; Zeit ein Strom über die Diode fließt (Reverse recovery current oder Freilaufstrom), muß die dadurch &#039;&#039;&#039;zusätzlich&#039;&#039;&#039; entstehende Verlustleistung in die obige Berechnung der maximalen Chiptemperatur mit einfließen.&lt;br /&gt;
&lt;br /&gt;
==Treiberleistung==&lt;br /&gt;
&lt;br /&gt;
Auch wenn der MOSFET ein spannungsgesteuertes Bauelement ist, muss trotzdem bei jedem Einschalten und bei jedem Ausschalten die Gatekapazität umgeladen werden. Bei älteren Leistungs-FET - oder bei einem schlechten Design (!) - muss sogar teilweise mit negativer Spannung am Gate gearbeitet werden, um eine vollständige Sperrung zu erreichen.&lt;br /&gt;
Diese Umladung muss möglichst schnell erfolgen, um die Verluste im FET während der Umschaltphase zu minimieren. Dazu findet ein [[Mosfet-Übersicht#MOSFET-Treiber|MOSFET-Treiber]] Verwendung. Hier eine detaillierte Beschreibung zum [[Treiber]].&lt;br /&gt;
&lt;br /&gt;
Da die Gatekapazität nicht direkt im Datenblatt enthalten ist kann man sich mit der Eingangskapazität Ciss behelfen. Im Arbeitspunkt ist die Gatekapazität ungefähr 5x größer als der im Datenblatt für Ciss angegebene Wert. &lt;br /&gt;
Daher berechnet sich die Treiberleistung wie folgt: &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{Treiber} = C \cdot U^2 \cdot f = 5 \cdot C_\text{íss} \cdot U_\text{Gate}^2 \cdot f_\text{schalt}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1.Beispiel, kleine MOSFET-Steuerung mit niedriger Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 4{,}8\,\text{nF} \cdot 15\,\text{V}^2 \cdot 10\,\text{kHz} = 54\,\text{mW}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2.Beispiel, sehr große MOSFET-Steuerung für Induktionsheizung mit sehr hoher Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 24\,\text{nF} \cdot 15\,\text{V}^2 \cdot 250\,\text{kHz} = 6{,}75\,\text{W}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung, so ein MOSFET-Treiber hat auch einen Eigenverbrauch, der leicht zwischen 0,5 und 1 W liegen kann.&lt;br /&gt;
&lt;br /&gt;
Bei niedrigen PWM-Frequenzen kann man Logic Level MOSFETs auch direkt per CMOS-Ausgang ansteuern, z.B. mit einem [[AVR]], wie in diesem [http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag] zu sehen ist.&lt;br /&gt;
&lt;br /&gt;
== Low- und High-Side ==&lt;br /&gt;
&lt;br /&gt;
Definition LS- und HS:&lt;br /&gt;
 &lt;br /&gt;
;Low-Side Schalter: Der FET schaltet eine Last gegen GND - auch als LS-Schalter bezeichnet.&lt;br /&gt;
;High-Side Schalter: Der FET schaltet eine Last an die Versorgungsspannung – auch als HS-Schalter bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Anregungen oder Fragen auch gerne per Email an [http://www.mikrocontroller.net/user/show/powerfreak Powerfreak]. Dieser Artikel kann dadurch regelmäßig erweitert und ggf. durch ein FAQ ergänzt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== SOA Diagramm ==&lt;br /&gt;
&lt;br /&gt;
SOA-Diagramm (engl. &#039;&#039;&#039;S&#039;&#039;&#039;afe &#039;&#039;&#039;O&#039;&#039;&#039;perating &#039;&#039;&#039;A&#039;&#039;&#039;rea, sicherer Arbeitsbereich) beschreibt die zulässige Verlustleistung eines Transistors in Anhängigkeit des Drainstroms (I_D), der Drain-Source Spannung (U_DS) und der Pulsbreite. Als Beispiel sei hier der BUZ 11 genannt. Im nachfolgenden Diagramm ist das SOA-Diagramm dargestellt. Wie ist es zu verstehen? Zunächst gibt es eine Grenze auf der linken Seite, die schräge, dunkelblaue Line. Diese wird durch den minimalen R_DS_ON festgelegt, hier wirkt der MOSFET wie ein ohmscher Widerstand. Mehr Strom kann bei einer bestimmten Spannung nicht fließen. Die zweite Grenzlinie ist ganz rechts die pinkfarbene Linie, sie stellt die maximale Sperrspannung des MOSFET dar. Die dritte Grenze ist der maximal zulässige Drainstrom, hier im Beispiel 120A, dargestellt durch die gelbe Linie. Die maximale Spannung zwischen Drain und Source sowie der Drainstrom sind abhängig von der Pulsbreite, mit welcher der MOSFET betrieben wird. Bei nur 2,5µs Pulsbreite (Rechteckimpuls) müssen die beiden Parameter sich innerhalb der Fläche bewegen, welche durch die dunkelblaue, gelbe und die pinkfarbene Line begrenzt wird. Im Extremfall dürfen 50V anliegen und 120A fließen, das sind satte 6kW Pulsleistung! Werden die Pulse breiter, so sinken die zulässigen Ströme und Spannungen, bei 1ms (dunkelblaue Linie bis zur braunen Linie, dann zur pinkfarbenen Linie) sind maximal noch 50V und 7A zulässig, also nur noch 350W. Die letzte Linie stellt den Fall für Gleichstrom (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent), also Dauerbelastung dar, hier sind bei 50V maximal 1,5A zulässig, was einer Dauerverlustleistung von 75W entspricht. MOSFETs, welche nur für Schaltbetrieb und nicht für [[#Linearbetrieb von MOSFETs | Linearbetrieb]] geeignet sind, haben keine Kennlinie für DC. Im normalen Schaltbetrieb liegt der Arbeitspunkt auf der linken Grenzlinie R_DS_ON_MIN. Nur im Linearbetrieb liegt der Arbeitspunkt innerhalb der Fläche, welche durch die Außenlinien begrenzt wird.&lt;br /&gt;
&lt;br /&gt;
[[bild: SOA-BUZ11.png | thumb | 300px| SOA-Diagramm]]&lt;br /&gt;
&lt;br /&gt;
Bei der Anwendung des Diagramms gilt es einiges zu beachten. Die Pulsleistungen sind nur zulässig, wenn der MOSFET vorher kalt ist, sprich ca. 25°C Sperrschichttemperatur hat. War er vorher schon heiß, reduziert sich die zulässige Belastung deutlich. Ebenso dürfen die Pulse nicht zu schnell wiederholt werden, denn dann ist der MOSFET noch vom vorherigen Puls aufgeheizt. Im Fall von DC sind 75W Verlustleistung auch eher ein theoretischer Wert, welcher real nur schwer erreicht werden kann, wenn der MOSFET auf einem sehr großen [[Kühlkörper]] optimal montiert ist. Praktisch liegen die erreichbaren Werte eher bei der Hälfte.&lt;br /&gt;
&lt;br /&gt;
(Anm. Eigentlich müsste für die R_DS_ON Grenzlinie R = U / I der minimale R_DS_ON rauskommen, hier ~40mOhm, es kommen aber ~80mOhm raus. Die Ursache dafür ist unklar, möglicherweise liegt hier ein Sicherheitsfaktor zu grunde).&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Linearbetrieb von MOSFETs ==&lt;br /&gt;
&lt;br /&gt;
Der Großteil der Anwendungen nutzt MOSFETs als Schalter, d.h. der MOSFET ist entweder voll gesperrt oder voll durchgesteuert. Dafür gelten auch all die Hinweise in diesem Artikel. In bestimmten Anwendungen werden MOSFETs aber auch im Linearbetrieb eingesetzt, z.B in linearen Endstufen für Audio, Video, elektronischen Lasten und Stromquellen. Hier muss man einiges beachten. Ein verbreiteter Irrtum besteht darin zu glauben, MOSFETs könne man im Linearbetrieb einfach parallel schalten, weil der positive Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; eine Symmetrierung bewirkt, ähnlich den Emitterwiderständen bei parallelgeschalteten Bipolartransistoren. Das ist &#039;&#039;ausschließlich&#039;&#039; im Schaltbetrieb möglich, und daher falsch! Im Linearbetrieb spielt der Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; keine Rolle, weil der MOSFET selten bis nie komplett durchgesteuert ist. Eben darum ist beim Linearbetrieb der minimale &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; in den meisten Fällen unwichtig und man kann auch eher hochohmige, ältere MOSFETs verwenden, wie z.B. den BUZ11.&lt;br /&gt;
&lt;br /&gt;
Hier wirkt vielmehr der negative Temperaturkoeffizient (TK) der Thresholdspannung &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, vergleichbar dem negativen TK der Basis-Emitter-Spannung von Bipolartransistoren. D.h. mit steigender Temperatur und konstanter Gate-Source-Spannung steigt der Stromfluss der Drain-Source Strecke. In einer Parallelschaltung von MOSFETs würde dies bedeuten, dass der MOSFET mit dem geringfügig größeren Drainstrom (Fertigungstoleranzen) wärmer wird, was zu einem weiter steigenden Drainstrom und damit noch mehr Wärme führt. Damit ist die Schaltung thermisch instabil und würde zum Durchbrennen der MOSFETs führen, einer nach dem Anderen. &lt;br /&gt;
&lt;br /&gt;
Um das zu verhindern muss man relativ große Ausgleichswiderstände in die Source-Leitung der einzelnen MOSFETs schalten, um diese Drift zu kompensieren. Dadurch verschlechtert sich natürlich der Wirkungsgrad des Verstärkers. MOSFETs haben einen TK von typisch -5mV/K für &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, das ist mehr als das doppelte von Bipolartransistoren mit typisch -2mV/K, weshalb die Symmetrierungswiderstände mehr als doppelt so groß sein müssen. Weiterhin muss man beachten, dass die Toleranzen von &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt; sehr groß sind, im Bereich von Volt! Das kann man sinnvoll nicht mehr mit Gatewiderständen symmetrieren, hier muss man die MOSFETs ausmessen und Gruppen mit geringen Toleranzen in einer Schaltung verwenden (engl. matching). &lt;br /&gt;
&lt;br /&gt;
Eine andere Möglichkeit ist die getrennte Ansteuerung der einzelnen MOSFETs, das wird oft in elektronischen Lasten bzw. [[Konstantstromquelle#Konstantstromquelle mit Operationsverstärker und Transistor | Konstantstromquellen]] gemacht. Hier treten keine zusätzlichen Verluste auf und der Mehraufwand in der Ansteuerung ist meist unkritisch.&lt;br /&gt;
&lt;br /&gt;
Weiterhin muss man beachten, dass viele der heutigen HochleistungsMOSFETs intern eine Parallelschaltung vieler kleiner MOSFET-Zellen (z.B. sogenannte Trench-FET) sind, und somit oft für den Linearbetrieb ungeeignet sind. Denn auch dort können einzelne Zellen überhitzen und durchbrennen (Hot Spot). Ob ein MOSFET für den Linearbetrieb tauglich ist steht manchmal im Datenblatt, oft aber eher nicht, eben weil die meisten MOSFETs als Schalter entwickelt und gebaut sind. Typische Vertreter für Linearbetrieb findet man in der [[MOSFET-Übersicht]]. Ein wichtiges Indiz für Linearbetrieb ist eine Kurve für DC im [[#SOA_Diagramm | SOA-Diagramm]]. Meist geht es dort nur bis 10ms, DC fehlt, eben weil DC (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent = Gleichstrom = Linearbetrieb) nicht zulässig ist. Manchmal hat der Hersteller auch &amp;quot;vergessen&amp;quot;, die Kennlinie für DC mit reinzuschreiben, wie z.B. bei [http://www.irf.com/product-info/hi-rel/alerts/fv5-p-09-01-A.pdf IRF], wie in diesem [http://www.mikrocontroller.net/topic/291760#3106758 Beitrag] zu erfahren ist.&lt;br /&gt;
Ein recht gutes Indiz dafür, ob ein FET für den Linearbetrieb taugt, ist die Vorwärtssteilheit. Diese kennzeichnet die Abhängigkeit des Drainstromes von der Ansteuerung am Gate als &amp;lt;math&amp;gt;S = \Delta i_d/\Delta u_{gs}&amp;lt;/math&amp;gt;. Moderne Trench-FET erreichen heute Steilheiten im dreistelligen Bereich und sind für Linearanwendungen völlig unbrauchbar. Zum Vergleich: Der BUZ11 kommt mit gerade einmal 4 bis 5 Siemens daher.&lt;br /&gt;
&lt;br /&gt;
In diesem Beitrag wird die DC-Linie im SOA-Diagramm noch genauer erklärt: [http://www.mikrocontroller.net/topic/319961#3473567 Re: MOSFET Linearbetrieb möglich?]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
* [[Mosfet-Übersicht]]&lt;br /&gt;
* [[IGBT]]&lt;br /&gt;
* [[TRIAC]]&lt;br /&gt;
* [[Kühlkörper]] &lt;br /&gt;
* [[Zwischenkreiskapazität]]&lt;br /&gt;
* [[Treiber]]&lt;br /&gt;
* [[Snippets#Wie_schlie.C3.9Fe_ich_einen_MOSFET_an_einen_Mikrocontroller_an.3F|Wie schließe ich einen Mosfet an einen Mikrocontroller an?]]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/H-Br%C3%BCcken_%C3%9Cbersicht Übersicht H-Brücken]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/168218#1609684 Forumsbeitrag]: MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/186785#new Forumsbeitrag]: nochmal MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/319961#3473567 Forumsbeitrag]: sehr ausführlicher Forumsbeitrag über MOSFETs im Linearbetrieb. Berücksichtigt auch den Spirito-Effekt.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/143324#new Forumsbeitrag]: Über eine elektronische Last, sehr lang&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag]: Logic Level MOSFETs direkt mit einem [[AVR]] treiben.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787855 Forumsbeitrag]: MOSFETs im Linearbetrieb, Laborerfahrungen&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787945 Forumsbeitrag]: MOSFETs für Linearbetrieb&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/269642?goto=2820617#2820617 Forumsbeitrag]: Verpol- und Überspannungsschutz mit MOSFETs&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/425414#4981611 Forumsbeitrag]: Linearbetrieb von MOSFETs&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.elektronikinfo.de/strom/feldeffekttransistoren.htm Feldeffekttransistoren bei elektronikinfo.de]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0207011.htm FET im ELKO]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0510161.htm MOSFET im ELKO]&lt;br /&gt;
* [http://www.sprut.de/electronic/switch/nkanal/nkanal.html MOSFET bei sprut.de]&lt;br /&gt;
* [http://sound.westhost.com/articles/hexfet.htm#51 MOSFETs in Audioendstufen, engl.]&lt;br /&gt;
* [http://irf.custhelp.com/cgi-bin/irf.cfg/php/enduser/std_adp.php?p_faqid=214&amp;amp;p_created=1019728945&amp;amp;p_sid=pt9ITiCj&amp;amp;p_accessibility=0&amp;amp;p_redirect=&amp;amp;p_lva=&amp;amp;p_sp=cF9zcmNoPTEmcF9zb3J0X2J5PSZwX2dyaWRzb3J0PSZwX3Jvd19jbnQ9MTQsMTQmcF9wcm9kcz0mcF9jYXRzPSZwX3B2PSZwX2N2PSZwX3BhZ2U9MSZwX3NlYXJjaF90ZXh0PWxpbmVhcg**&amp;amp;p_li=&amp;amp;p_topview=1 FAQ Answer ID 214 bei IRF zum Linearbetrieb]&lt;br /&gt;
* [http://www.nxp.com/documents/application_note/AN11158.pdf AN11158 - Understanding power MOSFET data sheet parameters] von NXP (PDF)&lt;br /&gt;
* [https://assets.nexperia.com/documents/technical-note/TN00008.pdf TN00008 - Power MOSFET frequently asked questions and answers] von nexperia (PDF)&lt;br /&gt;
* [http://www.infineon.com/dgdl/Infineon+-+Application+Note+-+PowerMOSFETs+-+OptiMOS%E2%84%A2+-+Linear+Mode+Operation+and+SOA+Power+MOSFETs.pdf?fileId=db3a30433e30e4bf013e3646e9381200 AN: Linear Mode Operation andSafe Operating Diagram of Power-MOSFETs] von Infineon (PDF)&lt;br /&gt;
* [http://www.ixys.com/Documents/Articles/Article_Linear_Power_MOSFETs.pdf MOSFETs Withstand Stress of Linear-Mode Operation] Neuentwickelte MOSFETs für Linearbetrieb (PDF)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]] [[Kategorie:Leistungselektronik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107028</id>
		<title>FET</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107028"/>
		<updated>2024-07-19T16:59:09Z</updated>

		<summary type="html">&lt;p&gt;Heha: Stromfühler-Anschluss&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel versteht sich als Unterpunkt zum Artikel [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein FET (engl. &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor) ist ein  Feldeffekttransistor. Der FET ist ein Bauelement, das im Gegensatz zum Bipolartransistor (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT) mit Spannung und nicht mit Strom gesteuert wird. Weil keine P-N-Sperrschichten am Stromfluss beteiligt sind, heißen diese auch Unipolartransistoren. Unterschieden werden&lt;br /&gt;
&lt;br /&gt;
* MOSFET = engl. &#039;&#039;&#039;M&#039;&#039;&#039;etall &#039;&#039;&#039;O&#039;&#039;&#039;xide &#039;&#039;&#039;S&#039;&#039;&#039;emiconductor &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor; Metalloxidschicht-FET, größte Teilgruppe der FETs mit isoliertem Gate.&lt;br /&gt;
&lt;br /&gt;
* JFET = engl. &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, Übergangszonen-FET, der steuerbare Kanal wird durch einen PN-Übergang wie in einer Diode gebildet. &amp;lt;small&amp;gt;(Diese Diode lässt sich nachmessen und unterscheidet JFET von (selbtleitenden) MOSFET, auch wenn sie schaltungstechnisch keine Rolle spielt. Wichtig: JFETs gibt es &#039;&#039;nur&#039;&#039; selbstleitend!)&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die drei Anschlüsse eines FETs werden &#039;&#039;Gate&#039;&#039;, &#039;&#039;Drain&#039;&#039; und &#039;&#039;Source&#039;&#039; genannt. Unter Umständen ist ein vierter Anschluß vorhanden, der &#039;&#039;Bulk&#039;&#039; genannt wird. Normalerweise ist Bulk intern mit Source verbunden. &amp;lt;small&amp;gt;Wenn dies nicht der Fall ist, muss diese Verbindung durch den Designer in der Schaltung hergestellt werden. Nicht gesichtet für Leistungstransistoren.&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== FET-Typen ==&lt;br /&gt;
&lt;br /&gt;
FETs werden hauptsächlich unterschieden in N-Kanal und P-Kanal, sowie &amp;quot;selbst sperrend = Anreicherungstyp&amp;quot; (engl. enhancement type) und &amp;quot;selbst leitend = Verarmungstyp&amp;quot; (engl. depletion type). Beim selbstleitenden FET ist der Transistor bei 0V Gate-Source Spannung maximal leitend (durchgesteuert) und wird durch Anlegen einer Spannung ans Gate gesperrt. Beim selbstsperrenden FET (größte Gruppe) ist der Transistor bei 0V Gate-Source Spannung gesperrt und wird durch Anlegen einer Spannung ans Gate leitend. Ist die Linie zwischen Drain und Source durchgezogen handelt es sich um einen selbstleitenden, bei einer gestrichelten Linie um einen selbstsperrenden FET. JFETs gibt es nur als Verarmungstyp.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Artikel wird nur der &#039;&#039;&#039;selbstsperrende&#039;&#039;&#039; MOSFET betrachtet, weil nur dieser einen althergebrachten Bipolartransistor „ersetzen“ kann.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Typen von Feldeffekttransistoren&amp;lt;br/&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Typ &lt;br /&gt;
! N-Kanal &lt;br /&gt;
! P-Kanal&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| JFET&amp;lt;br/&amp;gt;SFET&amp;lt;br/&amp;gt;(selbst leitend) || [[bild:JFET-N.png|center]]&lt;br /&gt;
* drittgrößte Gruppe&lt;br /&gt;
* bislang nur für kleine Leistungen verfügbar&lt;br /&gt;
* JFETs mit hoher Leistung sind im Kommen&lt;br /&gt;
* Eingangsstufen von OPVs&lt;br /&gt;
* Eingangsstufen von HF-Verstärkern bis in den GHz-Bereich&lt;br /&gt;
* als einfache [[Konstantstromquelle]] geeignet&lt;br /&gt;
| [[bild:JFET-P.png|center]]&lt;br /&gt;
* selten&lt;br /&gt;
* ersatzhalber für „fehlenden“ P-Kanal-Verarmungs-MOSFET&lt;br /&gt;
* Ältester Vertreter der Unipolartransistoren&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Anreicherungstyp&amp;lt;br/&amp;gt;(selbst sperrend)&amp;lt;br/&amp;gt;&#039;&#039;Enhancement Mode&#039;&#039; || [[bild:MOS-EN.png|center]]&lt;br /&gt;
* Mit Abstand größte Gruppe; wenn nicht besonders erwähnt ist &#039;&#039;dieser&#039;&#039; gemeint&lt;br /&gt;
* Eingangsstufen von HF-Verstärkern bis in den GHz-Bereich&lt;br /&gt;
** Typen mit zwei Gates zum Regeln und Mischen üblich&lt;br /&gt;
* Auch als Doppel-MOSFET oder zusammen mit P-Kanal-MOSFET in einem Gehäuse gängig&lt;br /&gt;
| [[bild:MOS-EP.png|center]]&lt;br /&gt;
* zweitgrößte Gruppe&lt;br /&gt;
* bei gleicher Geometrie etwas schlechter als ein N-Kanal-Typ, da „Löcherleitung“ (aus „Defektelektronen“ = gedachte positive Ladungsträger) vorliegt&lt;br /&gt;
* Auch als Doppel-MOSFET oder zusammen mit N-Kanal-MOSFET in einem Gehäuse gängig&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Verarmungstyp&amp;lt;br/&amp;gt;(selbst leitend)&amp;lt;br/&amp;gt;&#039;&#039;Depletion Mode&#039;&#039; || [[bild:MOS-DN.png|center]]&lt;br /&gt;
* selten aber käuflich&lt;br /&gt;
* Sinnvolle Anwendungen ergeben sich vor allem in Reihenschaltungen&lt;br /&gt;
| [[bild:MOS-DP.png|center]]&lt;br /&gt;
* Nicht in freier Wildbahn gesichtet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Vorteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Niedrigere Verluste als bei Bipolartransistoren.&lt;br /&gt;
* Sehr schnelles Schalten möglich, daher für sehr hohe Frequenzen geeignet (keine Speicherzeit wie beim BJT).&lt;br /&gt;
* Einfaches Parallelschalten im Schaltbetrieb, da Unterschiede im &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; durch den positiven Temperaturkoeffizienten ausgeglichen werden.&lt;br /&gt;
* Leistungslose Ansteuerung im statischen Fall, jedoch hohe Umladeverluste am Gate!&lt;br /&gt;
* oft preiswerter als vergleichbare Bipolartransistoren (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT)&lt;br /&gt;
* Relativ unempfindlich gegen Überspannung zwischen Drain und Source. Bei Überschreitung der Maximalspannung zwischen Drain und Source findet ein sogenannter &amp;quot;Durchbruch&amp;quot; statt. Dies ist vergleichbar mit dem Zener-Effekt. Ist die Energiemenge begrenzt, ist dieser Durchbruch reversibel und der FET wird NICHT zerstört. Beim Bipolartransistor ist die dafür zulässige Energiemenge viel kleiner, weil die den Strom durchquerende Fläche zu Hotspots neigt.&lt;br /&gt;
* Möglichkeit eines Stromfühler-Anschlusses: Durch geeignete Geometrie kann der Source-Anschluss aufgeteilt werden, etwa im Verhältnis 1:9 bis 1:99. Der Drainstrom teilt sich kontrolliert auf, auch wenn am „kleineren“ Source-Anschluss ein Messwiderstand zur Spannungs-Strom-Wandlung angeschlossen ist. Leider sind solche „Sense-MOSFETs“ selten einzeln und faktisch nie als P-Kanal-Typen verfügbar. In integrierten (Schaltnetzteil-)Schaltungen sind sie Standard.&lt;br /&gt;
&lt;br /&gt;
=== Nachteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Nur bedingt für hohe Spannungen [[Transistor#Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein ? |geeignet]], die ON-Verluste sind ab ca. 250V höher als bei einem [[IGBT]]. &lt;br /&gt;
* Parasitäre Diode parallel zur Drain-Source Strecke ist immer enthalten, das (Ab-)Schaltverhalten dieser Dioden ist meist schlechter als separate Dioden, was häufig zu unerwünschten Schwingungen führt.&lt;br /&gt;
* Empfindlicher gegen ESD am Gate als BJT&lt;br /&gt;
* Positiver Temperaturkoeffizient (TK), der &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; ist stark temperaturabhängig und steigt von 25°C (Datenblattangabe) auf 150°C ungefähr um den Faktor 2. Dadurch steigen auch die Verluste und damit die Erwärmung des Bauteiles.&lt;br /&gt;
&lt;br /&gt;
=== Erklärung der wichtigsten Datenblattwerte ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:7em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | Beispiel&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Drain Source Voltage &amp;lt;br&amp;gt;(Breakdown) || V(BR)_DSS &amp;lt;br&amp;gt; V_DS || 75 V || Maximale Spannungsfestigkeit des Bauteiles zwischen Drain und Source&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Drain Current  || I_D(on)   || 55 A @ 125 °C  || Maximaler Dauerstrom bei 125°C Gehäusetemperatur &lt;br /&gt;
|-&lt;br /&gt;
| Pulsed Drain Current || ID_pulse &amp;lt;br&amp;gt; I_CD(on) || 240 A || Maximaler Pulsstrom (Achtung die zulässige Zeitdauer des Pulses kann nur über die maximale Junctiontemperatur ermittelt werden)&lt;br /&gt;
|-&lt;br /&gt;
| Gate Charge || Q&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; ||  320 nC || Gate-Ladung in Coloumb oder Amperesekunden, die zum Ein- oder Ausschalten des MOSFET erforderlich ist. Dies berücksichtigt die Nichtlinearität der Kapazität und (richtig?) den Miller-Effekt. Mit dieser Angabe wird der für das Schalten von Leistungs-MOSFET benötigte Gatestrom für eine bestimmte Schaltzeit ermittelt. Dieser liegt üblicherweise im einstelligen Ampere-Bereich. Moderne Typen mit extra geringer Gate-Ladung können so schneller schalten.&lt;br /&gt;
|-&lt;br /&gt;
| Repetetive Avalanche Energy || t_sc ||  280 mJ || Maximale Energie, welche beim Avalanche Durchbruch bei Überschreiten der maximalen Drain-Source Spannung im MOSFET bei z.&amp;amp;nbsp;B. 1% Puls/Pausen Verhältnis regelmäßig auftreten darf, ohne den FET zu schädigen&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_ON&amp;lt;/sub&amp;gt; ||  0,01 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;25°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_on&amp;lt;/sub&amp;gt; ||  0,021 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;175°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Thermal Resistance&amp;lt;br&amp;gt;(junction-case) ||  R&amp;lt;sub&amp;gt;th_JC&amp;lt;/sub&amp;gt; ||  0,8 K/W || Thermischer Widerstand im Transistor vom der „Sperrschicht“ (junction eines Bipolartransistors, ja altmodischer Index, hier besser „Kanal“) bis zur Rückseite oder bestmöglichen Kühlmöglichkeit des Transistorgehäuses (case)&lt;br /&gt;
|-&lt;br /&gt;
| Gate-Source&amp;lt;br&amp;gt;Threshold Voltage ||  V_GS(th) || 2,0-4,5 V || Gatespannung, ab welcher der Transistor minimal leitend wird (I_D typisch 100-200µA), große Toleranz, typisch 1:2 zwischen Minimum und Maximum&lt;br /&gt;
|-&lt;br /&gt;
| Turn-on Delay ||  t_d(on) || 40 ns ||  Verzögerung zwischen dem Einschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Rise Time ||  t_r || 200 ns ||  Anstiegszeit des Transistorstromes am Drain&lt;br /&gt;
|-&lt;br /&gt;
| Turn-off Delay || t_d(off) || 120 ns ||  Verzögerung zwischen Abschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Fall Time ||  t_f  || 60 ns || Abfallzeit des Transistorstromes am Drain  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die oben genannten Zeiten gelten ausschließlich unter den angegebenen Messbedingungen (Gatewiderstand, Treiberspannung, sowie einer &#039;&#039;&#039;FET-Teperatur von 25°C!&#039;&#039;&#039;) und müssen für die eigene Anwendung ggf. neu berechnet werden. Meist wird man sie eher messen, weil die Rechung zu aufwändig und bisweilen unmöglich ist. &lt;br /&gt;
&lt;br /&gt;
==== Gate-Source Threshold Voltage ====&lt;br /&gt;
Gerade bei der &#039;&#039;&#039;Gate-Source Threshold Voltage &amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt;&#039;&#039;&#039; gibt es hier immer wieder Verwirrung. Sie gibt an, ab welcher Spannung der MOSFET anfängt, minimal leitfähig zu werden. Diese Spannung ist technologisch bedingt auch heute noch einer starken Toleranz unterworfen, typischerweise hat der Bereich  bei dem der FET zu leiten beginnt eine Spreizung von etwa 1:2 zwischen Minimum und Maximum. Welche Spannung man nun wirklich anlegen muss, um den gewünschten &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt; zu erreichen kann der Tabelle im jeweiligen Datenblatt entnommen werden. Dabei unbedingt die angegebene Gatespannung beachten, nur dieser Wert ist garantiert!.&lt;br /&gt;
&lt;br /&gt;
[[bild: IRLZ34N_R_DS_ON.png | thumb | 800px | R_DS_ON im Datenblatt des IRLZ34N]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Die Kurvenschar von &amp;lt;math&amp;gt;I_D&amp;lt;/math&amp;gt; über &amp;lt;math&amp;gt;U_{DS}&amp;lt;/math&amp;gt; in Abhängigkeit von &amp;lt;math&amp;gt;U_{GS}&amp;lt;/math&amp;gt; stellt immer nur typische Werte dar, keine garantierten Extremwerte (engl. worst case). Als Standardwerte kann man 10-15V für einen Standardtypen und ca. 3-5V für einen Logic Level MOSFET (LL-FET) ansetzen. Kleinsignal-FETs leiten schon ab ca 1V. Bei Ansteuerung mit 5V benötigt man also einen Typen, der &#039;&#039;&#039;sicher&#039;&#039;&#039; bei 5V voll durchgesteuert ist, z.B. IRLZ34N. Bei 3,3V ist er bereits nicht mehr zuverlässig nutzbar. Es gibt auch Typen mit noch geringerer Spannung für Vollaussteuerung. Wer einen BUZ11 (&amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt; 4V max.) mit 5V ansteuert riskiert ein Abfackeln des MOSFETs, denn je nach Toleranz kann er bereit ganz gut aufgesteuert sein oder auch nicht. Das soll in den nachfolgenden Diagrammen dargestellt werden. Zunächst die Transferkennlinie. Sie gibt an, wieviel Drainstrom in Abhängigkeit der Gatespannung fließen kann, wobei die Drainspannung konstant ist, hier im Beispiel 25V. Ein typischer BUZ11 (mittlere, schwarze Kurve) fängt bei 3V zu leiten an und erreicht bei 6V am Gate ca. 17A. Erwischt man nun ein kritisches Exemplar, das erst bei 4V zu leiten anfängt (rechte, rote Kurve um 1V parallel verschoben), so kann dieser bei 6V nur 8A leiten, für 17A braucht er 7V. Der günstige Fall, daß der BUZ11 schon bei 2V anfängt zu leiten sieht man in der linken, roten Kurve.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UGS_ID.png | thumb | 800px | Transferkennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Wenn man die drei Fälle (min, typ, max) mit 5V ansteuert, erhält man die Arbeitspunkte 1-3 mit einem maximal schaltbaren Drainstrom von 2,5, 8 und 17A. Diese kann man in das Ausgangskennlinienfeld übertragen. Wir nehmen max. 1V U_DS an. Es ergeben sie die gleichen Ströme. Der maximale Drainstrom weicht um -5,5/+9A vom typischen Fall ab. Erhöht man nun die Gate-Source Spannung auf 10V (Arbeitspunkt 4), schwankt der maximal schaltbare Drainstrom nur noch um -2/+2A, außerdem liegt er mit 30A deutlich höher. Der BUZ11 ist somit sicher durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UDS_ID_UGS.png | thumb | 800px | Ausgangskennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
==== Parasitäre Diode ====&lt;br /&gt;
&lt;br /&gt;
Der Schwerpunkt in der FET-Entwicklung liegt auf den geringst-möglichen &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt;,  die Diode entsteht auf Grund des Herstellungsprozesses, und wird nur nachrangig verbessert, da viele Optimierungsversuche auch einen Einfluss auf wichtige Kennwerte des FETs hatten und haben.&lt;br /&gt;
Daher muss sorgfältig geprüft werden, ob die Schaltgeschwindigkeit, die Recovery-Time und die damit verbundenen Verluste sowie die dabei erzeugte unerwünschte EMV-Abstahlung tolerierbar ist, oder nicht.&lt;br /&gt;
Hier hilft es oft eine optimierte Diode / Schottky-Diode zum FET parallel zu schalten. Ganz ausblenden läßt sich die parasitäre Diode jedoch nicht, jedoch kann man den Anteil des Stromes beeinflussen, der über die intere Diode fliest.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039; Parasitäre Diode des FETs  &#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Current  ||  I_S || 75A || Maximaler Dauerstrom der parasitären Diode, meist identisch zum maximalen Dauerstrom des MOSFETs&lt;br /&gt;
|-&lt;br /&gt;
| Forward Voltage ||  V_SD || 1,3V || Spannungsfall an der parasitären Diode &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Time ||  t_rr || 120ns || Zeit, welche die Elektronen brauchen um aus der leitenden Diode vollständig abzufließen. Während dieser Zeit fließt der Strom in &#039;&#039;&#039;Rückwärtsrichtung&#039;&#039;&#039; durch die Diode und erzeugt relativ viel Verlustleistung. &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Charge ||  Q_rr || 60nC || Ladungsmenge, die während t_rr rückwärts durch die Diode fließt.  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Haupttypen und Gatespannungslevel ==&lt;br /&gt;
&lt;br /&gt;
===Unterschied N-Kanal / P-Kanal FET===&lt;br /&gt;
&lt;br /&gt;
Im Schaltsymbol werden die MOSFET-Typen meist durch die Pfeilrichtung in der Mitte des Symbols (eigentlich &amp;quot;Bulk&amp;quot;) vom oder zum Gate unterschieden.  Zeigt der Pfeil zum Gate hin, handelt es sich um einen N-Kanal-FET, zeigt der Pfeil vom Gate weg um einen P-Kanal FET.&lt;br /&gt;
&lt;br /&gt;
Der große Vorteil des N-Kanal FETs (Elektronenleitung) ist, daß er immer niederohmiger ist, als ein gleich großer P-Kanal FET (Löcherleitung). Daher sind P-Kanal Typen bei vergleichbaren Werten auch immer größer = teuerer da weniger Chips auf einem Wafer Platz haben.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET muss die Gatespannung positiv gegenüber Source sein. Dabei wird der FET dann leitend, wenn die sogenannte &amp;quot;threshold voltage&amp;quot; (Schwellenspannung) erreicht wird. Eine typische Anwendung ist z.&amp;amp;nbsp;B. ein &#039;&#039;&#039;Low-Side Schalter&#039;&#039;&#039;: Source an GND, Drain an die Last, Ansteuerung des N-Kanal FETs mit 12V gleichbedeutend mit 12V ÜBER den Source = GND Potential.&lt;br /&gt;
 &lt;br /&gt;
Beim P-Kanal FET als HS-Schalter muss die Gatespannung negativer=niedriger als das Sourcepotential sein.Beispiel.&lt;br /&gt;
Beispiel:  &lt;br /&gt;
Lastspannung = 400V d.h. Source an 400V, Last zwischen Drain und GND, Ansteuerung des P-Kanal FETs mit 388V, also 12V UNTER dem Sourcepotential.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET als HS-Schalter muss die Gatespannung positver=höher als das Sourcepotential sein.&lt;br /&gt;
Beispiel:&lt;br /&gt;
Lastspannung = 400V d.h. Drain an 400V, die Last zwischen Source und GND, Ansteuerung des N-Kanal FETs mit 412V, also 12V ÜBER dem Sourcepotential.&lt;br /&gt;
In diesem Fall ist aber eine zusätzliche Spannungsquelle erforderlich, denn der FET wird mit einer Spannung über der Lastspannung eingeschaltet. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weblinks&#039;&#039;&#039;&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-1-of-2/?utm_source=eetimes&amp;amp;utm_medium=networksearch A primer on high-side FET load switches (Part 1 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products, Micrel, Inc., 5/3/2007 4:14 PM EDT, www.eetimes.com&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-2-of-2/ A primer on high-side FET load switches (Part 2 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products. Micrel, Inc., 5/7/2007 1:36 PM EDT, www.eetimes.com&lt;br /&gt;
* [http://www.vishay.com/docs/70611/70611.pdf AN804 P-Channel MOSFETs, the Best Choice for High-Side Switching (PDF)] von Vishay Siliconix&lt;br /&gt;
&lt;br /&gt;
===Unterschied Logic-Level / &amp;quot;Normal&amp;quot;-Level===&lt;br /&gt;
&lt;br /&gt;
Den meisten FETs ist gemein, daß sie mit einer Spannung von 10..15V angesteuert werden müssen, um den minimalen Einschaltwiderstand zu erreichen. Diese FETs lassen sich nicht ohne weiteres mit einem CMOS-Pegel von 5V ansteuern. Es gibt jedoch für diesen Anwendungsfall sogenannte &amp;quot;Logic Level&amp;quot; (LL) FETs, die schon bei einer Gatespannung von etwa 4,5V voll durchgesteuert sind. Einige Kleinsignal-FETs sind schon ab ca. 1,2V voll durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
== Beispiel zur Bauteiledimensionierung ==&lt;br /&gt;
&lt;br /&gt;
=== Spannungsfestigkeit ===&lt;br /&gt;
&lt;br /&gt;
Die höchste vorkommende Betriebsspannung + Abschaltüberspannung soll kleiner als ca. 80% der Spannungsfestigkeit des Bauteiles sein. &lt;br /&gt;
&lt;br /&gt;
Achtung: Zwischen dem je nach Anwendungsfall erforderlichen Pufferkondensator und dem FET wird es immer eine parasitäre Induktivität geben.&lt;br /&gt;
Abhängig von Schaltgeschwindigkeit und Induktivität wird im Schaltmoment eine mehr oder weniger große Übrspannungsspitze produziert. Dieser Peak&lt;br /&gt;
addiert sich auf die aktuelle Versorgungsspannung.&lt;br /&gt;
&lt;br /&gt;
Überschlagsrechnung als Beispiel:&lt;br /&gt;
* Schaltgeschwindigkeit:  dI/dt = -100A/µs (= Abschalten von 5A innerhalb 50ns),&lt;br /&gt;
* Induktivität:   L = 1µH (~ 1 m loses, ungebündeltes Kabel)&lt;br /&gt;
* dU=-L*dI/dt = -1µH * (-100A / 1µs) = 100V&lt;br /&gt;
&lt;br /&gt;
Dies bedeutet, daß an der &amp;quot;Induktivität&amp;quot; zwischen Transistor und Kondensator - Aufgrund von Selbstinduktion im Schaltmoment - ein Überspannungspuls von ca. 100V entsteht, der auf die Betriebsspannung aufzuschlagen ist, also lieber die Leitung kürzer machen, und - sofern möglich - nicht ganz so schnell schalten.&lt;br /&gt;
&lt;br /&gt;
=== Stromtragfähigkeit ===&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt ist eine Stromtragfähigkeit bei 25°C, und meist noch bei einer höheren Temperatur z.B. 125°C, 150°C oder 175°C Kühlfahnentemperatur angegeben. Dieser Wert ist als ERSTE Entscheidungsgrundlage ausreichend, aber aus der theoretisch abführbaren Verlustleistung errechnet, und&lt;br /&gt;
* dient zum qualitativen Vergleich von Transistoren bezüglich ihres R_ds(on) und ihres Wärmewiderstands.&lt;br /&gt;
* ist für die Dimensionierung einer Schaltung nur als Richtwert zu interpretieren. &lt;br /&gt;
* ist ohne Schaltverluste genannt, und daher nur für einen Schaltbetrieb von wenigen Hz gültig. Außerdem wird ein annähernd idealer Kühlkörper unterstellt, der trotz der Verlustleistung das Gehäuse des Transistors auf der angegebenen Temperatur halten kann.&lt;br /&gt;
* entbindet einen nicht davon den Kopf einzuschalten... siehe die nachfolgenden Zeilen.&lt;br /&gt;
* Liegt der Strom für den die Schaltung entwickelt wird mit ca. 10..20% Abstand unter dem Datenblattwert von 125°C ist dieses Bauteil vermutlich verwendbar (siehe Detailberechnungen unten !).   &lt;br /&gt;
* Ist der benötigte Strom im Bereich oder größer als der zulässige bei 125°C sollte entweder ein anderer Typ eingesetzt oder mehrere FETs &#039;&#039;des gleichen Typs&#039;&#039; parallelgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
== Verlustleistung ==&lt;br /&gt;
&lt;br /&gt;
Hier wird eine Näherung für eine getaktete Anwendung betrachtet. In einem Transistor treten sowohl beim Ein- und Ausschalten, als auch während der Einschaltphase Verluste im Bauteil auf. Diese Verluste führen zu einer Bauteilerwärmung. Die dabei entstehende Temperatur darf die maximal zulässige Bauteiletemperatur nie überschreiten. Bei den ersten Projekten ist zu empfehlen eine berechnete Chiptemperatur von ca. 125°C nicht zu überschreiten. Fast alle aktuell verfügbaren FETs nennen im Datenblatt eine Temperatur von 175°C als ihre maximale Chiptemperatur.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:32em&amp;quot; &lt;br /&gt;
|+ &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Symbol&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Wert&lt;br /&gt;
|-&lt;br /&gt;
| Betriebsspannung || U&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 70 V&lt;br /&gt;
|-&lt;br /&gt;
| Nennstrom || I&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 30 A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source Widerstand bei&amp;lt;br&amp;gt;Chiptemperatur: 125°C&amp;lt;br&amp;gt; Gatespannung: 10V || R&amp;lt;sub&amp;gt;DS&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt;&amp;lt;/sub&amp;gt; || 17 mΩ&lt;br /&gt;
|-&lt;br /&gt;
| Pulsbreite || t&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt; || 150 µs&lt;br /&gt;
|-&lt;br /&gt;
| Schaltfrequenz || ƒ&amp;lt;sub&amp;gt;schalt&amp;lt;/sub&amp;gt; || 5 kHz&amp;lt;br&amp;gt;T = 200µs&lt;br /&gt;
|-&lt;br /&gt;
| Einschaltzeit (risetime) || t&amp;lt;sub&amp;gt;r&amp;lt;/sub&amp;gt; || 500 ns&lt;br /&gt;
|-&lt;br /&gt;
| Ausschaltzeit (falltime) || t&amp;lt;sub&amp;gt;ƒ&amp;lt;/sub&amp;gt; || 800 ns &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Leitend-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Während der FET bei [[PWM]]-Ansteuerung eingeschaltet ist, erzeugt er Verlustleistung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
P_\text{ON}&lt;br /&gt;
 = {I_\mathrm{N}}^2 \cdot R_\mathrm{DS_\mathrm{ON}} \cdot \frac{t_\mathrm{ON}}{T}&lt;br /&gt;
 = 30^2A^2 \cdot 17m\Omega  \cdot \frac{150\mu s}{200\mu s} = 11{,}5W&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schalt-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Vereinfachter Ansatz.&lt;br /&gt;
&lt;br /&gt;
Einschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_r}&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_r}{T} \\&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot 70V \cdot 30A \cdot \frac{500ns}{200\mu s}=1{,}3W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_f} &lt;br /&gt;
&amp;amp;=\tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_f}{T}\\&lt;br /&gt;
&amp;amp;=\tfrac14 \cdot 70V \cdot 30A \cdot \frac{800ns}{200\mu s}=2{,}1W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ und genauer kann man rechnen, wenn die Ein- Ausschaltenergie im Datenblatt angegeben ist. Aber Achtung! Die  Randbedingungen unter denen die genannte Energie ermittelt wurde, müssen genau so zutreffen.&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_f} = f_{schalt} \cdot E_{ON}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_r} = f_{schalt} \cdot E_{OFF}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Gesamtverlustleistung beträgt also in etwa 15W.&lt;br /&gt;
&lt;br /&gt;
Damit muß ein entsprechender [[Kühlkörper]] ausgelegt und die Chiptemperatur berechnet werden. z.&amp;amp;nbsp;B.:&lt;br /&gt;
* Kühlkörper mit einem R_th von 0,2K/W&lt;br /&gt;
* max. Umgebungstemperatur +60°C&lt;br /&gt;
* R_th &amp;quot;junction-case&amp;quot; des FETs 0,8K/W&lt;br /&gt;
* R_th der Wärmeleitfolie zwischen FET und Kühlkörper ca. 2,0K/W&lt;br /&gt;
* R_th gesamt: 3,0K/W &amp;lt;br&amp;gt;&lt;br /&gt;
* Bei einer Verlustleistung von 18W und einer Umgebungstemperatur von 60°C hat der Chip eine Temperatur von ca. 18W * 3,0K/W +60°C = 114°C. ==&amp;gt; o.k.!&lt;br /&gt;
&lt;br /&gt;
Unter Berücksichtigung der Tatsache, daß hier viele Vereinfachungen vorgenommen, und die Art der Last nicht beachtet wurde ist es sinnvoll, einen gewissen Sicherheitsabstand zu den zulässigen Maximalwerten einzuhalten. Daher ist es empfehlenswert, die Chiptemperatur auf ca. 125°C zu beschränken. &lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist hier die parasitäre Diode im FET nicht berücksichtigt.&lt;br /&gt;
Wenn während der &amp;quot;off&amp;quot; Zeit ein Strom über die Diode fließt (Reverse recovery current oder Freilaufstrom), muß die dadurch &#039;&#039;&#039;zusätzlich&#039;&#039;&#039; entstehende Verlustleistung in die obige Berechnung der maximalen Chiptemperatur mit einfließen.&lt;br /&gt;
&lt;br /&gt;
==Treiberleistung==&lt;br /&gt;
&lt;br /&gt;
Auch wenn der MOSFET ein spannungsgesteuertes Bauelement ist, muss trotzdem bei jedem Einschalten und bei jedem Ausschalten die Gatekapazität umgeladen werden. Bei älteren Leistungs-FET - oder bei einem schlechten Design (!) - muss sogar teilweise mit negativer Spannung am Gate gearbeitet werden, um eine vollständige Sperrung zu erreichen.&lt;br /&gt;
Diese Umladung muss möglichst schnell erfolgen, um die Verluste im FET während der Umschaltphase zu minimieren. Dazu findet ein [[Mosfet-Übersicht#MOSFET-Treiber|MOSFET-Treiber]] Verwendung. Hier eine detaillierte Beschreibung zum [[Treiber]].&lt;br /&gt;
&lt;br /&gt;
Da die Gatekapazität nicht direkt im Datenblatt enthalten ist kann man sich mit der Eingangskapazität Ciss behelfen. Im Arbeitspunkt ist die Gatekapazität ungefähr 5x größer als der im Datenblatt für Ciss angegebene Wert. &lt;br /&gt;
Daher berechnet sich die Treiberleistung wie folgt: &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{Treiber} = C \cdot U^2 \cdot f = 5 \cdot C_\text{íss} \cdot U_\text{Gate}^2 \cdot f_\text{schalt}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1.Beispiel, kleine MOSFET-Steuerung mit niedriger Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 4{,}8\,\text{nF} \cdot 15\,\text{V}^2 \cdot 10\,\text{kHz} = 54\,\text{mW}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2.Beispiel, sehr große MOSFET-Steuerung für Induktionsheizung mit sehr hoher Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 24\,\text{nF} \cdot 15\,\text{V}^2 \cdot 250\,\text{kHz} = 6{,}75\,\text{W}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung, so ein MOSFET-Treiber hat auch einen Eigenverbrauch, der leicht zwischen 0,5 und 1 W liegen kann.&lt;br /&gt;
&lt;br /&gt;
Bei niedrigen PWM-Frequenzen kann man Logic Level MOSFETs auch direkt per CMOS-Ausgang ansteuern, z.B. mit einem [[AVR]], wie in diesem [http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag] zu sehen ist.&lt;br /&gt;
&lt;br /&gt;
== Low- und High-Side ==&lt;br /&gt;
&lt;br /&gt;
Definition LS- und HS:&lt;br /&gt;
 &lt;br /&gt;
;Low-Side Schalter: Der FET schaltet eine Last gegen GND - auch als LS-Schalter bezeichnet.&lt;br /&gt;
;High-Side Schalter: Der FET schaltet eine Last an die Versorgungsspannung – auch als HS-Schalter bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Anregungen oder Fragen auch gerne per Email an [http://www.mikrocontroller.net/user/show/powerfreak Powerfreak]. Dieser Artikel kann dadurch regelmäßig erweitert und ggf. durch ein FAQ ergänzt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== SOA Diagramm ==&lt;br /&gt;
&lt;br /&gt;
SOA-Diagramm (engl. &#039;&#039;&#039;S&#039;&#039;&#039;afe &#039;&#039;&#039;O&#039;&#039;&#039;perating &#039;&#039;&#039;A&#039;&#039;&#039;rea, sicherer Arbeitsbereich) beschreibt die zulässige Verlustleistung eines Transistors in Anhängigkeit des Drainstroms (I_D), der Drain-Source Spannung (U_DS) und der Pulsbreite. Als Beispiel sei hier der BUZ 11 genannt. Im nachfolgenden Diagramm ist das SOA-Diagramm dargestellt. Wie ist es zu verstehen? Zunächst gibt es eine Grenze auf der linken Seite, die schräge, dunkelblaue Line. Diese wird durch den minimalen R_DS_ON festgelegt, hier wirkt der MOSFET wie ein ohmscher Widerstand. Mehr Strom kann bei einer bestimmten Spannung nicht fließen. Die zweite Grenzlinie ist ganz rechts die pinkfarbene Linie, sie stellt die maximale Sperrspannung des MOSFET dar. Die dritte Grenze ist der maximal zulässige Drainstrom, hier im Beispiel 120A, dargestellt durch die gelbe Linie. Die maximale Spannung zwischen Drain und Source sowie der Drainstrom sind abhängig von der Pulsbreite, mit welcher der MOSFET betrieben wird. Bei nur 2,5µs Pulsbreite (Rechteckimpuls) müssen die beiden Parameter sich innerhalb der Fläche bewegen, welche durch die dunkelblaue, gelbe und die pinkfarbene Line begrenzt wird. Im Extremfall dürfen 50V anliegen und 120A fließen, das sind satte 6kW Pulsleistung! Werden die Pulse breiter, so sinken die zulässigen Ströme und Spannungen, bei 1ms (dunkelblaue Linie bis zur braunen Linie, dann zur pinkfarbenen Linie) sind maximal noch 50V und 7A zulässig, also nur noch 350W. Die letzte Linie stellt den Fall für Gleichstrom (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent), also Dauerbelastung dar, hier sind bei 50V maximal 1,5A zulässig, was einer Dauerverlustleistung von 75W entspricht. MOSFETs, welche nur für Schaltbetrieb und nicht für [[#Linearbetrieb von MOSFETs | Linearbetrieb]] geeignet sind, haben keine Kennlinie für DC. Im normalen Schaltbetrieb liegt der Arbeitspunkt auf der linken Grenzlinie R_DS_ON_MIN. Nur im Linearbetrieb liegt der Arbeitspunkt innerhalb der Fläche, welche durch die Außenlinien begrenzt wird.&lt;br /&gt;
&lt;br /&gt;
[[bild: SOA-BUZ11.png | thumb | 300px| SOA-Diagramm]]&lt;br /&gt;
&lt;br /&gt;
Bei der Anwendung des Diagramms gilt es einiges zu beachten. Die Pulsleistungen sind nur zulässig, wenn der MOSFET vorher kalt ist, sprich ca. 25°C Sperrschichttemperatur hat. War er vorher schon heiß, reduziert sich die zulässige Belastung deutlich. Ebenso dürfen die Pulse nicht zu schnell wiederholt werden, denn dann ist der MOSFET noch vom vorherigen Puls aufgeheizt. Im Fall von DC sind 75W Verlustleistung auch eher ein theoretischer Wert, welcher real nur schwer erreicht werden kann, wenn der MOSFET auf einem sehr großen [[Kühlkörper]] optimal montiert ist. Praktisch liegen die erreichbaren Werte eher bei der Hälfte.&lt;br /&gt;
&lt;br /&gt;
(Anm. Eigentlich müsste für die R_DS_ON Grenzlinie R = U / I der minimale R_DS_ON rauskommen, hier ~40mOhm, es kommen aber ~80mOhm raus. Die Ursache dafür ist unklar, möglicherweise liegt hier ein Sicherheitsfaktor zu grunde).&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Linearbetrieb von MOSFETs ==&lt;br /&gt;
&lt;br /&gt;
Der Großteil der Anwendungen nutzt MOSFETs als Schalter, d.h. der MOSFET ist entweder voll gesperrt oder voll durchgesteuert. Dafür gelten auch all die Hinweise in diesem Artikel. In bestimmten Anwendungen werden MOSFETs aber auch im Linearbetrieb eingesetzt, z.B in linearen Endstufen für Audio, Video, elektronischen Lasten und Stromquellen. Hier muss man einiges beachten. Ein verbreiteter Irrtum besteht darin zu glauben, MOSFETs könne man im Linearbetrieb einfach parallel schalten, weil der positive Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; eine Symmetrierung bewirkt, ähnlich den Emitterwiderständen bei parallelgeschalteten Bipolartransistoren. Das ist &#039;&#039;ausschließlich&#039;&#039; im Schaltbetrieb möglich, und daher falsch! Im Linearbetrieb spielt der Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; keine Rolle, weil der MOSFET selten bis nie komplett durchgesteuert ist. Eben darum ist beim Linearbetrieb der minimale &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; in den meisten Fällen unwichtig und man kann auch eher hochohmige, ältere MOSFETs verwenden, wie z.B. den BUZ11.&lt;br /&gt;
&lt;br /&gt;
Hier wirkt vielmehr der negative Temperaturkoeffizient (TK) der Thresholdspannung &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, vergleichbar dem negativen TK der Basis-Emitter-Spannung von Bipolartransistoren. D.h. mit steigender Temperatur und konstanter Gate-Source-Spannung steigt der Stromfluss der Drain-Source Strecke. In einer Parallelschaltung von MOSFETs würde dies bedeuten, dass der MOSFET mit dem geringfügig größeren Drainstrom (Fertigungstoleranzen) wärmer wird, was zu einem weiter steigenden Drainstrom und damit noch mehr Wärme führt. Damit ist die Schaltung thermisch instabil und würde zum Durchbrennen der MOSFETs führen, einer nach dem Anderen. &lt;br /&gt;
&lt;br /&gt;
Um das zu verhindern muss man relativ große Ausgleichswiderstände in die Source-Leitung der einzelnen MOSFETs schalten, um diese Drift zu kompensieren. Dadurch verschlechtert sich natürlich der Wirkungsgrad des Verstärkers. MOSFETs haben einen TK von typisch -5mV/K für &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, das ist mehr als das doppelte von Bipolartransistoren mit typisch -2mV/K, weshalb die Symmetrierungswiderstände mehr als doppelt so groß sein müssen. Weiterhin muss man beachten, dass die Toleranzen von &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt; sehr groß sind, im Bereich von Volt! Das kann man sinnvoll nicht mehr mit Gatewiderständen symmetrieren, hier muss man die MOSFETs ausmessen und Gruppen mit geringen Toleranzen in einer Schaltung verwenden (engl. matching). &lt;br /&gt;
&lt;br /&gt;
Eine andere Möglichkeit ist die getrennte Ansteuerung der einzelnen MOSFETs, das wird oft in elektronischen Lasten bzw. [[Konstantstromquelle#Konstantstromquelle mit Operationsverstärker und Transistor | Konstantstromquellen]] gemacht. Hier treten keine zusätzlichen Verluste auf und der Mehraufwand in der Ansteuerung ist meist unkritisch.&lt;br /&gt;
&lt;br /&gt;
Weiterhin muss man beachten, dass viele der heutigen HochleistungsMOSFETs intern eine Parallelschaltung vieler kleiner MOSFET-Zellen (z.B. sogenannte Trench-FET) sind, und somit oft für den Linearbetrieb ungeeignet sind. Denn auch dort können einzelne Zellen überhitzen und durchbrennen (Hot Spot). Ob ein MOSFET für den Linearbetrieb tauglich ist steht manchmal im Datenblatt, oft aber eher nicht, eben weil die meisten MOSFETs als Schalter entwickelt und gebaut sind. Typische Vertreter für Linearbetrieb findet man in der [[MOSFET-Übersicht]]. Ein wichtiges Indiz für Linearbetrieb ist eine Kurve für DC im [[#SOA_Diagramm | SOA-Diagramm]]. Meist geht es dort nur bis 10ms, DC fehlt, eben weil DC (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent = Gleichstrom = Linearbetrieb) nicht zulässig ist. Manchmal hat der Hersteller auch &amp;quot;vergessen&amp;quot;, die Kennlinie für DC mit reinzuschreiben, wie z.B. bei [http://www.irf.com/product-info/hi-rel/alerts/fv5-p-09-01-A.pdf IRF], wie in diesem [http://www.mikrocontroller.net/topic/291760#3106758 Beitrag] zu erfahren ist.&lt;br /&gt;
Ein recht gutes Indiz dafür, ob ein FET für den Linearbetrieb taugt, ist die Vorwärtssteilheit. Diese kennzeichnet die Abhängigkeit des Drainstromes von der Ansteuerung am Gate als &amp;lt;math&amp;gt;S = \Delta i_d/\Delta u_{gs}&amp;lt;/math&amp;gt;. Moderne Trench-FET erreichen heute Steilheiten im dreistelligen Bereich und sind für Linearanwendungen völlig unbrauchbar. Zum Vergleich: Der BUZ11 kommt mit gerade einmal 4 bis 5 Siemens daher.&lt;br /&gt;
&lt;br /&gt;
In diesem Beitrag wird die DC-Linie im SOA-Diagramm noch genauer erklärt: [http://www.mikrocontroller.net/topic/319961#3473567 Re: MOSFET Linearbetrieb möglich?]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
* [[Mosfet-Übersicht]]&lt;br /&gt;
* [[IGBT]]&lt;br /&gt;
* [[TRIAC]]&lt;br /&gt;
* [[Kühlkörper]] &lt;br /&gt;
* [[Zwischenkreiskapazität]]&lt;br /&gt;
* [[Treiber]]&lt;br /&gt;
* [[Snippets#Wie_schlie.C3.9Fe_ich_einen_MOSFET_an_einen_Mikrocontroller_an.3F|Wie schließe ich einen Mosfet an einen Mikrocontroller an?]]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/H-Br%C3%BCcken_%C3%9Cbersicht Übersicht H-Brücken]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/168218#1609684 Forumsbeitrag]: MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/186785#new Forumsbeitrag]: nochmal MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/319961#3473567 Forumsbeitrag]: sehr ausführlicher Forumsbeitrag über MOSFETs im Linearbetrieb. Berücksichtigt auch den Spirito-Effekt.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/143324#new Forumsbeitrag]: Über eine elektronische Last, sehr lang&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag]: Logic Level MOSFETs direkt mit einem [[AVR]] treiben.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787855 Forumsbeitrag]: MOSFETs im Linearbetrieb, Laborerfahrungen&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787945 Forumsbeitrag]: MOSFETs für Linearbetrieb&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/269642?goto=2820617#2820617 Forumsbeitrag]: Verpol- und Überspannungsschutz mit MOSFETs&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/425414#4981611 Forumsbeitrag]: Linearbetrieb von MOSFETs&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.elektronikinfo.de/strom/feldeffekttransistoren.htm Feldeffekttransistoren bei elektronikinfo.de]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0207011.htm FET im ELKO]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0510161.htm MOSFET im ELKO]&lt;br /&gt;
* [http://www.sprut.de/electronic/switch/nkanal/nkanal.html MOSFET bei sprut.de]&lt;br /&gt;
* [http://sound.westhost.com/articles/hexfet.htm#51 MOSFETs in Audioendstufen, engl.]&lt;br /&gt;
* [http://irf.custhelp.com/cgi-bin/irf.cfg/php/enduser/std_adp.php?p_faqid=214&amp;amp;p_created=1019728945&amp;amp;p_sid=pt9ITiCj&amp;amp;p_accessibility=0&amp;amp;p_redirect=&amp;amp;p_lva=&amp;amp;p_sp=cF9zcmNoPTEmcF9zb3J0X2J5PSZwX2dyaWRzb3J0PSZwX3Jvd19jbnQ9MTQsMTQmcF9wcm9kcz0mcF9jYXRzPSZwX3B2PSZwX2N2PSZwX3BhZ2U9MSZwX3NlYXJjaF90ZXh0PWxpbmVhcg**&amp;amp;p_li=&amp;amp;p_topview=1 FAQ Answer ID 214 bei IRF zum Linearbetrieb]&lt;br /&gt;
* [http://www.nxp.com/documents/application_note/AN11158.pdf AN11158 - Understanding power MOSFET data sheet parameters] von NXP (PDF)&lt;br /&gt;
* [https://assets.nexperia.com/documents/technical-note/TN00008.pdf TN00008 - Power MOSFET frequently asked questions and answers] von nexperia (PDF)&lt;br /&gt;
* [http://www.infineon.com/dgdl/Infineon+-+Application+Note+-+PowerMOSFETs+-+OptiMOS%E2%84%A2+-+Linear+Mode+Operation+and+SOA+Power+MOSFETs.pdf?fileId=db3a30433e30e4bf013e3646e9381200 AN: Linear Mode Operation andSafe Operating Diagram of Power-MOSFETs] von Infineon (PDF)&lt;br /&gt;
* [http://www.ixys.com/Documents/Articles/Article_Linear_Power_MOSFETs.pdf MOSFETs Withstand Stress of Linear-Mode Operation] Neuentwickelte MOSFETs für Linearbetrieb (PDF)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]] [[Kategorie:Leistungselektronik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107027</id>
		<title>FET</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107027"/>
		<updated>2024-07-19T16:52:23Z</updated>

		<summary type="html">&lt;p&gt;Heha: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel versteht sich als Unterpunkt zum Artikel [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein FET (engl. &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor) ist ein  Feldeffekttransistor. Der FET ist ein Bauelement, das im Gegensatz zum Bipolartransistor (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT) mit Spannung und nicht mit Strom gesteuert wird. Weil keine P-N-Sperrschichten am Stromfluss beteiligt sind, heißen diese auch Unipolartransistoren. Unterschieden werden&lt;br /&gt;
&lt;br /&gt;
* MOSFET = engl. &#039;&#039;&#039;M&#039;&#039;&#039;etall &#039;&#039;&#039;O&#039;&#039;&#039;xide &#039;&#039;&#039;S&#039;&#039;&#039;emiconductor &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor; Metalloxidschicht-FET, größte Teilgruppe der FETs mit isoliertem Gate.&lt;br /&gt;
&lt;br /&gt;
* JFET = engl. &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, Übergangszonen-FET, der steuerbare Kanal wird durch einen PN-Übergang wie in einer Diode gebildet. &amp;lt;small&amp;gt;(Diese Diode lässt sich nachmessen und unterscheidet JFET von (selbtleitenden) MOSFET, auch wenn sie schaltungstechnisch keine Rolle spielt. Wichtig: JFETs gibt es &#039;&#039;nur&#039;&#039; selbstleitend!)&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die drei Anschlüsse eines FETs werden &#039;&#039;Gate&#039;&#039;, &#039;&#039;Drain&#039;&#039; und &#039;&#039;Source&#039;&#039; genannt. Unter Umständen ist ein vierter Anschluß vorhanden, der &#039;&#039;Bulk&#039;&#039; genannt wird. Normalerweise ist Bulk intern mit Source verbunden. &amp;lt;small&amp;gt;Wenn dies nicht der Fall ist, muss diese Verbindung durch den Designer in der Schaltung hergestellt werden. Nicht gesichtet für Leistungstransistoren.&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== FET-Typen ==&lt;br /&gt;
&lt;br /&gt;
FETs werden hauptsächlich unterschieden in N-Kanal und P-Kanal, sowie &amp;quot;selbst sperrend = Anreicherungstyp&amp;quot; (engl. enhancement type) und &amp;quot;selbst leitend = Verarmungstyp&amp;quot; (engl. depletion type). Beim selbstleitenden FET ist der Transistor bei 0V Gate-Source Spannung maximal leitend (durchgesteuert) und wird durch Anlegen einer Spannung ans Gate gesperrt. Beim selbstsperrenden FET (größte Gruppe) ist der Transistor bei 0V Gate-Source Spannung gesperrt und wird durch Anlegen einer Spannung ans Gate leitend. Ist die Linie zwischen Drain und Source durchgezogen handelt es sich um einen selbstleitenden, bei einer gestrichelten Linie um einen selbstsperrenden FET. JFETs gibt es nur als Verarmungstyp.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Artikel wird nur der &#039;&#039;&#039;selbstsperrende&#039;&#039;&#039; MOSFET betrachtet, weil nur dieser einen althergebrachten Bipolartransistor „ersetzen“ kann.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Typen von Feldeffekttransistoren&amp;lt;br/&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Typ &lt;br /&gt;
! N-Kanal &lt;br /&gt;
! P-Kanal&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| JFET&amp;lt;br/&amp;gt;SFET&amp;lt;br/&amp;gt;(selbst leitend) || [[bild:JFET-N.png|center]]&lt;br /&gt;
* drittgrößte Gruppe&lt;br /&gt;
* bislang nur für kleine Leistungen verfügbar&lt;br /&gt;
* JFETs mit hoher Leistung sind im Kommen&lt;br /&gt;
* Eingangsstufen von OPVs&lt;br /&gt;
* Eingangsstufen von HF-Verstärkern bis in den GHz-Bereich&lt;br /&gt;
* als einfache [[Konstantstromquelle]] geeignet&lt;br /&gt;
| [[bild:JFET-P.png|center]]&lt;br /&gt;
* selten&lt;br /&gt;
* ersatzhalber für „fehlenden“ P-Kanal-Verarmungs-MOSFET&lt;br /&gt;
* Ältester Vertreter der Unipolartransistoren&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Anreicherungstyp&amp;lt;br/&amp;gt;(selbst sperrend)&amp;lt;br/&amp;gt;&#039;&#039;Enhancement Mode&#039;&#039; || [[bild:MOS-EN.png|center]]&lt;br /&gt;
* Mit Abstand größte Gruppe; wenn nicht besonders erwähnt ist &#039;&#039;dieser&#039;&#039; gemeint&lt;br /&gt;
* Eingangsstufen von HF-Verstärkern bis in den GHz-Bereich&lt;br /&gt;
** Typen mit zwei Gates zum Regeln und Mischen üblich&lt;br /&gt;
* Auch als Doppel-MOSFET oder zusammen mit P-Kanal-MOSFET in einem Gehäuse gängig&lt;br /&gt;
| [[bild:MOS-EP.png|center]]&lt;br /&gt;
* zweitgrößte Gruppe&lt;br /&gt;
* bei gleicher Geometrie etwas schlechter als ein N-Kanal-Typ, da „Löcherleitung“ (aus „Defektelektronen“ = gedachte positive Ladungsträger) vorliegt&lt;br /&gt;
* Auch als Doppel-MOSFET oder zusammen mit N-Kanal-MOSFET in einem Gehäuse gängig&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Verarmungstyp&amp;lt;br/&amp;gt;(selbst leitend)&amp;lt;br/&amp;gt;&#039;&#039;Depletion Mode&#039;&#039; || [[bild:MOS-DN.png|center]]&lt;br /&gt;
* selten aber käuflich&lt;br /&gt;
* Sinnvolle Anwendungen ergeben sich vor allem in Reihenschaltungen&lt;br /&gt;
| [[bild:MOS-DP.png|center]]&lt;br /&gt;
* Nicht in freier Wildbahn gesichtet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Vorteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Niedrigere Verluste als bei Bipolartransistoren.&lt;br /&gt;
* Sehr schnelles Schalten möglich, daher für sehr hohe Frequenzen geeignet (keine Speicherzeit wie beim BJT).&lt;br /&gt;
* Einfaches Parallelschalten im Schaltbetrieb, da Unterschiede im &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; durch den positiven Temperaturkoeffizienten ausgeglichen werden.&lt;br /&gt;
* Leistungslose Ansteuerung im statischen Fall, jedoch hohe Umladeverluste am Gate!&lt;br /&gt;
* oft preiswerter als vergleichbare Bipolartransistoren (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT)&lt;br /&gt;
* Relativ unempfindlich gegen Überspannung zwischen Drain und Source. Bei Überschreitung der Maximalspannung zwischen Drain und Source findet ein sogenannter &amp;quot;Durchbruch&amp;quot; statt. Dies ist vergleichbar mit dem Zener-Effekt. Ist die Energiemenge begrenzt, ist dieser Durchbruch reversibel und der FET wird NICHT zerstört. Beim Bipolartransistor ist die dafür zulässige Energiemenge viel kleiner, weil die den Strom durchquerende Fläche zu Hotspots neigt.&lt;br /&gt;
&lt;br /&gt;
=== Nachteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Nur bedingt für hohe Spannungen [[Transistor#Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein ? |geeignet]], die ON-Verluste sind ab ca. 250V höher als bei einem [[IGBT]]. &lt;br /&gt;
* Parasitäre Diode parallel zur Drain-Source Strecke ist immer enthalten, das (Ab-)Schaltverhalten dieser Dioden ist meist schlechter als separate Dioden, was häufig zu unerwünschten Schwingungen führt.&lt;br /&gt;
* Empfindlicher gegen ESD am Gate als BJT&lt;br /&gt;
* Positiver Temperaturkoeffizient (TK), der &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; ist stark temperaturabhängig und steigt von 25°C (Datenblattangabe) auf 150°C ungefähr um den Faktor 2. Dadurch steigen auch die Verluste und damit die Erwärmung des Bauteiles.&lt;br /&gt;
&lt;br /&gt;
=== Erklärung der wichtigsten Datenblattwerte ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:7em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | Beispiel&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Drain Source Voltage &amp;lt;br&amp;gt;(Breakdown) || V(BR)_DSS &amp;lt;br&amp;gt; V_DS || 75 V || Maximale Spannungsfestigkeit des Bauteiles zwischen Drain und Source&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Drain Current  || I_D(on)   || 55 A @ 125 °C  || Maximaler Dauerstrom bei 125°C Gehäusetemperatur &lt;br /&gt;
|-&lt;br /&gt;
| Pulsed Drain Current || ID_pulse &amp;lt;br&amp;gt; I_CD(on) || 240 A || Maximaler Pulsstrom (Achtung die zulässige Zeitdauer des Pulses kann nur über die maximale Junctiontemperatur ermittelt werden)&lt;br /&gt;
|-&lt;br /&gt;
| Gate Charge || Q&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; ||  320 nC || Gate-Ladung in Coloumb oder Amperesekunden, die zum Ein- oder Ausschalten des MOSFET erforderlich ist. Dies berücksichtigt die Nichtlinearität der Kapazität und (richtig?) den Miller-Effekt. Mit dieser Angabe wird der für das Schalten von Leistungs-MOSFET benötigte Gatestrom für eine bestimmte Schaltzeit ermittelt. Dieser liegt üblicherweise im einstelligen Ampere-Bereich. Moderne Typen mit extra geringer Gate-Ladung können so schneller schalten.&lt;br /&gt;
|-&lt;br /&gt;
| Repetetive Avalanche Energy || t_sc ||  280 mJ || Maximale Energie, welche beim Avalanche Durchbruch bei Überschreiten der maximalen Drain-Source Spannung im MOSFET bei z.&amp;amp;nbsp;B. 1% Puls/Pausen Verhältnis regelmäßig auftreten darf, ohne den FET zu schädigen&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_ON&amp;lt;/sub&amp;gt; ||  0,01 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;25°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_on&amp;lt;/sub&amp;gt; ||  0,021 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;175°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Thermal Resistance&amp;lt;br&amp;gt;(junction-case) ||  R&amp;lt;sub&amp;gt;th_JC&amp;lt;/sub&amp;gt; ||  0,8 K/W || Thermischer Widerstand im Transistor vom der „Sperrschicht“ (junction eines Bipolartransistors, ja altmodischer Index, hier besser „Kanal“) bis zur Rückseite oder bestmöglichen Kühlmöglichkeit des Transistorgehäuses (case)&lt;br /&gt;
|-&lt;br /&gt;
| Gate-Source&amp;lt;br&amp;gt;Threshold Voltage ||  V_GS(th) || 2,0-4,5 V || Gatespannung, ab welcher der Transistor minimal leitend wird (I_D typisch 100-200µA), große Toleranz, typisch 1:2 zwischen Minimum und Maximum&lt;br /&gt;
|-&lt;br /&gt;
| Turn-on Delay ||  t_d(on) || 40 ns ||  Verzögerung zwischen dem Einschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Rise Time ||  t_r || 200 ns ||  Anstiegszeit des Transistorstromes am Drain&lt;br /&gt;
|-&lt;br /&gt;
| Turn-off Delay || t_d(off) || 120 ns ||  Verzögerung zwischen Abschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Fall Time ||  t_f  || 60 ns || Abfallzeit des Transistorstromes am Drain  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die oben genannten Zeiten gelten ausschließlich unter den angegebenen Messbedingungen (Gatewiderstand, Treiberspannung, sowie einer &#039;&#039;&#039;FET-Teperatur von 25°C!&#039;&#039;&#039;) und müssen für die eigene Anwendung ggf. neu berechnet werden. Meist wird man sie eher messen, weil die Rechung zu aufwändig und bisweilen unmöglich ist. &lt;br /&gt;
&lt;br /&gt;
==== Gate-Source Threshold Voltage ====&lt;br /&gt;
Gerade bei der &#039;&#039;&#039;Gate-Source Threshold Voltage &amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt;&#039;&#039;&#039; gibt es hier immer wieder Verwirrung. Sie gibt an, ab welcher Spannung der MOSFET anfängt, minimal leitfähig zu werden. Diese Spannung ist technologisch bedingt auch heute noch einer starken Toleranz unterworfen, typischerweise hat der Bereich  bei dem der FET zu leiten beginnt eine Spreizung von etwa 1:2 zwischen Minimum und Maximum. Welche Spannung man nun wirklich anlegen muss, um den gewünschten &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt; zu erreichen kann der Tabelle im jeweiligen Datenblatt entnommen werden. Dabei unbedingt die angegebene Gatespannung beachten, nur dieser Wert ist garantiert!.&lt;br /&gt;
&lt;br /&gt;
[[bild: IRLZ34N_R_DS_ON.png | thumb | 800px | R_DS_ON im Datenblatt des IRLZ34N]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Die Kurvenschar von &amp;lt;math&amp;gt;I_D&amp;lt;/math&amp;gt; über &amp;lt;math&amp;gt;U_{DS}&amp;lt;/math&amp;gt; in Abhängigkeit von &amp;lt;math&amp;gt;U_{GS}&amp;lt;/math&amp;gt; stellt immer nur typische Werte dar, keine garantierten Extremwerte (engl. worst case). Als Standardwerte kann man 10-15V für einen Standardtypen und ca. 3-5V für einen Logic Level MOSFET (LL-FET) ansetzen. Kleinsignal-FETs leiten schon ab ca 1V. Bei Ansteuerung mit 5V benötigt man also einen Typen, der &#039;&#039;&#039;sicher&#039;&#039;&#039; bei 5V voll durchgesteuert ist, z.B. IRLZ34N. Bei 3,3V ist er bereits nicht mehr zuverlässig nutzbar. Es gibt auch Typen mit noch geringerer Spannung für Vollaussteuerung. Wer einen BUZ11 (&amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt; 4V max.) mit 5V ansteuert riskiert ein Abfackeln des MOSFETs, denn je nach Toleranz kann er bereit ganz gut aufgesteuert sein oder auch nicht. Das soll in den nachfolgenden Diagrammen dargestellt werden. Zunächst die Transferkennlinie. Sie gibt an, wieviel Drainstrom in Abhängigkeit der Gatespannung fließen kann, wobei die Drainspannung konstant ist, hier im Beispiel 25V. Ein typischer BUZ11 (mittlere, schwarze Kurve) fängt bei 3V zu leiten an und erreicht bei 6V am Gate ca. 17A. Erwischt man nun ein kritisches Exemplar, das erst bei 4V zu leiten anfängt (rechte, rote Kurve um 1V parallel verschoben), so kann dieser bei 6V nur 8A leiten, für 17A braucht er 7V. Der günstige Fall, daß der BUZ11 schon bei 2V anfängt zu leiten sieht man in der linken, roten Kurve.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UGS_ID.png | thumb | 800px | Transferkennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Wenn man die drei Fälle (min, typ, max) mit 5V ansteuert, erhält man die Arbeitspunkte 1-3 mit einem maximal schaltbaren Drainstrom von 2,5, 8 und 17A. Diese kann man in das Ausgangskennlinienfeld übertragen. Wir nehmen max. 1V U_DS an. Es ergeben sie die gleichen Ströme. Der maximale Drainstrom weicht um -5,5/+9A vom typischen Fall ab. Erhöht man nun die Gate-Source Spannung auf 10V (Arbeitspunkt 4), schwankt der maximal schaltbare Drainstrom nur noch um -2/+2A, außerdem liegt er mit 30A deutlich höher. Der BUZ11 ist somit sicher durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UDS_ID_UGS.png | thumb | 800px | Ausgangskennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
==== Parasitäre Diode ====&lt;br /&gt;
&lt;br /&gt;
Der Schwerpunkt in der FET-Entwicklung liegt auf den geringst-möglichen &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt;,  die Diode entsteht auf Grund des Herstellungsprozesses, und wird nur nachrangig verbessert, da viele Optimierungsversuche auch einen Einfluss auf wichtige Kennwerte des FETs hatten und haben.&lt;br /&gt;
Daher muss sorgfältig geprüft werden, ob die Schaltgeschwindigkeit, die Recovery-Time und die damit verbundenen Verluste sowie die dabei erzeugte unerwünschte EMV-Abstahlung tolerierbar ist, oder nicht.&lt;br /&gt;
Hier hilft es oft eine optimierte Diode / Schottky-Diode zum FET parallel zu schalten. Ganz ausblenden läßt sich die parasitäre Diode jedoch nicht, jedoch kann man den Anteil des Stromes beeinflussen, der über die intere Diode fliest.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039; Parasitäre Diode des FETs  &#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Current  ||  I_S || 75A || Maximaler Dauerstrom der parasitären Diode, meist identisch zum maximalen Dauerstrom des MOSFETs&lt;br /&gt;
|-&lt;br /&gt;
| Forward Voltage ||  V_SD || 1,3V || Spannungsfall an der parasitären Diode &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Time ||  t_rr || 120ns || Zeit, welche die Elektronen brauchen um aus der leitenden Diode vollständig abzufließen. Während dieser Zeit fließt der Strom in &#039;&#039;&#039;Rückwärtsrichtung&#039;&#039;&#039; durch die Diode und erzeugt relativ viel Verlustleistung. &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Charge ||  Q_rr || 60nC || Ladungsmenge, die während t_rr rückwärts durch die Diode fließt.  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Haupttypen und Gatespannungslevel ==&lt;br /&gt;
&lt;br /&gt;
===Unterschied N-Kanal / P-Kanal FET===&lt;br /&gt;
&lt;br /&gt;
Im Schaltsymbol werden die MOSFET-Typen meist durch die Pfeilrichtung in der Mitte des Symbols (eigentlich &amp;quot;Bulk&amp;quot;) vom oder zum Gate unterschieden.  Zeigt der Pfeil zum Gate hin, handelt es sich um einen N-Kanal-FET, zeigt der Pfeil vom Gate weg um einen P-Kanal FET.&lt;br /&gt;
&lt;br /&gt;
Der große Vorteil des N-Kanal FETs (Elektronenleitung) ist, daß er immer niederohmiger ist, als ein gleich großer P-Kanal FET (Löcherleitung). Daher sind P-Kanal Typen bei vergleichbaren Werten auch immer größer = teuerer da weniger Chips auf einem Wafer Platz haben.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET muss die Gatespannung positiv gegenüber Source sein. Dabei wird der FET dann leitend, wenn die sogenannte &amp;quot;threshold voltage&amp;quot; (Schwellenspannung) erreicht wird. Eine typische Anwendung ist z.&amp;amp;nbsp;B. ein &#039;&#039;&#039;Low-Side Schalter&#039;&#039;&#039;: Source an GND, Drain an die Last, Ansteuerung des N-Kanal FETs mit 12V gleichbedeutend mit 12V ÜBER den Source = GND Potential.&lt;br /&gt;
 &lt;br /&gt;
Beim P-Kanal FET als HS-Schalter muss die Gatespannung negativer=niedriger als das Sourcepotential sein.Beispiel.&lt;br /&gt;
Beispiel:  &lt;br /&gt;
Lastspannung = 400V d.h. Source an 400V, Last zwischen Drain und GND, Ansteuerung des P-Kanal FETs mit 388V, also 12V UNTER dem Sourcepotential.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET als HS-Schalter muss die Gatespannung positver=höher als das Sourcepotential sein.&lt;br /&gt;
Beispiel:&lt;br /&gt;
Lastspannung = 400V d.h. Drain an 400V, die Last zwischen Source und GND, Ansteuerung des N-Kanal FETs mit 412V, also 12V ÜBER dem Sourcepotential.&lt;br /&gt;
In diesem Fall ist aber eine zusätzliche Spannungsquelle erforderlich, denn der FET wird mit einer Spannung über der Lastspannung eingeschaltet. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weblinks&#039;&#039;&#039;&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-1-of-2/?utm_source=eetimes&amp;amp;utm_medium=networksearch A primer on high-side FET load switches (Part 1 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products, Micrel, Inc., 5/3/2007 4:14 PM EDT, www.eetimes.com&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-2-of-2/ A primer on high-side FET load switches (Part 2 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products. Micrel, Inc., 5/7/2007 1:36 PM EDT, www.eetimes.com&lt;br /&gt;
* [http://www.vishay.com/docs/70611/70611.pdf AN804 P-Channel MOSFETs, the Best Choice for High-Side Switching (PDF)] von Vishay Siliconix&lt;br /&gt;
&lt;br /&gt;
===Unterschied Logic-Level / &amp;quot;Normal&amp;quot;-Level===&lt;br /&gt;
&lt;br /&gt;
Den meisten FETs ist gemein, daß sie mit einer Spannung von 10..15V angesteuert werden müssen, um den minimalen Einschaltwiderstand zu erreichen. Diese FETs lassen sich nicht ohne weiteres mit einem CMOS-Pegel von 5V ansteuern. Es gibt jedoch für diesen Anwendungsfall sogenannte &amp;quot;Logic Level&amp;quot; (LL) FETs, die schon bei einer Gatespannung von etwa 4,5V voll durchgesteuert sind. Einige Kleinsignal-FETs sind schon ab ca. 1,2V voll durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
== Beispiel zur Bauteiledimensionierung ==&lt;br /&gt;
&lt;br /&gt;
=== Spannungsfestigkeit ===&lt;br /&gt;
&lt;br /&gt;
Die höchste vorkommende Betriebsspannung + Abschaltüberspannung soll kleiner als ca. 80% der Spannungsfestigkeit des Bauteiles sein. &lt;br /&gt;
&lt;br /&gt;
Achtung: Zwischen dem je nach Anwendungsfall erforderlichen Pufferkondensator und dem FET wird es immer eine parasitäre Induktivität geben.&lt;br /&gt;
Abhängig von Schaltgeschwindigkeit und Induktivität wird im Schaltmoment eine mehr oder weniger große Übrspannungsspitze produziert. Dieser Peak&lt;br /&gt;
addiert sich auf die aktuelle Versorgungsspannung.&lt;br /&gt;
&lt;br /&gt;
Überschlagsrechnung als Beispiel:&lt;br /&gt;
* Schaltgeschwindigkeit:  dI/dt = -100A/µs (= Abschalten von 5A innerhalb 50ns),&lt;br /&gt;
* Induktivität:   L = 1µH (~ 1 m loses, ungebündeltes Kabel)&lt;br /&gt;
* dU=-L*dI/dt = -1µH * (-100A / 1µs) = 100V&lt;br /&gt;
&lt;br /&gt;
Dies bedeutet, daß an der &amp;quot;Induktivität&amp;quot; zwischen Transistor und Kondensator - Aufgrund von Selbstinduktion im Schaltmoment - ein Überspannungspuls von ca. 100V entsteht, der auf die Betriebsspannung aufzuschlagen ist, also lieber die Leitung kürzer machen, und - sofern möglich - nicht ganz so schnell schalten.&lt;br /&gt;
&lt;br /&gt;
=== Stromtragfähigkeit ===&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt ist eine Stromtragfähigkeit bei 25°C, und meist noch bei einer höheren Temperatur z.B. 125°C, 150°C oder 175°C Kühlfahnentemperatur angegeben. Dieser Wert ist als ERSTE Entscheidungsgrundlage ausreichend, aber aus der theoretisch abführbaren Verlustleistung errechnet, und&lt;br /&gt;
* dient zum qualitativen Vergleich von Transistoren bezüglich ihres R_ds(on) und ihres Wärmewiderstands.&lt;br /&gt;
* ist für die Dimensionierung einer Schaltung nur als Richtwert zu interpretieren. &lt;br /&gt;
* ist ohne Schaltverluste genannt, und daher nur für einen Schaltbetrieb von wenigen Hz gültig. Außerdem wird ein annähernd idealer Kühlkörper unterstellt, der trotz der Verlustleistung das Gehäuse des Transistors auf der angegebenen Temperatur halten kann.&lt;br /&gt;
* entbindet einen nicht davon den Kopf einzuschalten... siehe die nachfolgenden Zeilen.&lt;br /&gt;
* Liegt der Strom für den die Schaltung entwickelt wird mit ca. 10..20% Abstand unter dem Datenblattwert von 125°C ist dieses Bauteil vermutlich verwendbar (siehe Detailberechnungen unten !).   &lt;br /&gt;
* Ist der benötigte Strom im Bereich oder größer als der zulässige bei 125°C sollte entweder ein anderer Typ eingesetzt oder mehrere FETs &#039;&#039;des gleichen Typs&#039;&#039; parallelgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
== Verlustleistung ==&lt;br /&gt;
&lt;br /&gt;
Hier wird eine Näherung für eine getaktete Anwendung betrachtet. In einem Transistor treten sowohl beim Ein- und Ausschalten, als auch während der Einschaltphase Verluste im Bauteil auf. Diese Verluste führen zu einer Bauteilerwärmung. Die dabei entstehende Temperatur darf die maximal zulässige Bauteiletemperatur nie überschreiten. Bei den ersten Projekten ist zu empfehlen eine berechnete Chiptemperatur von ca. 125°C nicht zu überschreiten. Fast alle aktuell verfügbaren FETs nennen im Datenblatt eine Temperatur von 175°C als ihre maximale Chiptemperatur.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:32em&amp;quot; &lt;br /&gt;
|+ &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Symbol&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Wert&lt;br /&gt;
|-&lt;br /&gt;
| Betriebsspannung || U&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 70 V&lt;br /&gt;
|-&lt;br /&gt;
| Nennstrom || I&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 30 A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source Widerstand bei&amp;lt;br&amp;gt;Chiptemperatur: 125°C&amp;lt;br&amp;gt; Gatespannung: 10V || R&amp;lt;sub&amp;gt;DS&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt;&amp;lt;/sub&amp;gt; || 17 mΩ&lt;br /&gt;
|-&lt;br /&gt;
| Pulsbreite || t&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt; || 150 µs&lt;br /&gt;
|-&lt;br /&gt;
| Schaltfrequenz || ƒ&amp;lt;sub&amp;gt;schalt&amp;lt;/sub&amp;gt; || 5 kHz&amp;lt;br&amp;gt;T = 200µs&lt;br /&gt;
|-&lt;br /&gt;
| Einschaltzeit (risetime) || t&amp;lt;sub&amp;gt;r&amp;lt;/sub&amp;gt; || 500 ns&lt;br /&gt;
|-&lt;br /&gt;
| Ausschaltzeit (falltime) || t&amp;lt;sub&amp;gt;ƒ&amp;lt;/sub&amp;gt; || 800 ns &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Leitend-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Während der FET bei [[PWM]]-Ansteuerung eingeschaltet ist, erzeugt er Verlustleistung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
P_\text{ON}&lt;br /&gt;
 = {I_\mathrm{N}}^2 \cdot R_\mathrm{DS_\mathrm{ON}} \cdot \frac{t_\mathrm{ON}}{T}&lt;br /&gt;
 = 30^2A^2 \cdot 17m\Omega  \cdot \frac{150\mu s}{200\mu s} = 11{,}5W&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schalt-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Vereinfachter Ansatz.&lt;br /&gt;
&lt;br /&gt;
Einschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_r}&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_r}{T} \\&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot 70V \cdot 30A \cdot \frac{500ns}{200\mu s}=1{,}3W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_f} &lt;br /&gt;
&amp;amp;=\tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_f}{T}\\&lt;br /&gt;
&amp;amp;=\tfrac14 \cdot 70V \cdot 30A \cdot \frac{800ns}{200\mu s}=2{,}1W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ und genauer kann man rechnen, wenn die Ein- Ausschaltenergie im Datenblatt angegeben ist. Aber Achtung! Die  Randbedingungen unter denen die genannte Energie ermittelt wurde, müssen genau so zutreffen.&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_f} = f_{schalt} \cdot E_{ON}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_r} = f_{schalt} \cdot E_{OFF}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Gesamtverlustleistung beträgt also in etwa 15W.&lt;br /&gt;
&lt;br /&gt;
Damit muß ein entsprechender [[Kühlkörper]] ausgelegt und die Chiptemperatur berechnet werden. z.&amp;amp;nbsp;B.:&lt;br /&gt;
* Kühlkörper mit einem R_th von 0,2K/W&lt;br /&gt;
* max. Umgebungstemperatur +60°C&lt;br /&gt;
* R_th &amp;quot;junction-case&amp;quot; des FETs 0,8K/W&lt;br /&gt;
* R_th der Wärmeleitfolie zwischen FET und Kühlkörper ca. 2,0K/W&lt;br /&gt;
* R_th gesamt: 3,0K/W &amp;lt;br&amp;gt;&lt;br /&gt;
* Bei einer Verlustleistung von 18W und einer Umgebungstemperatur von 60°C hat der Chip eine Temperatur von ca. 18W * 3,0K/W +60°C = 114°C. ==&amp;gt; o.k.!&lt;br /&gt;
&lt;br /&gt;
Unter Berücksichtigung der Tatsache, daß hier viele Vereinfachungen vorgenommen, und die Art der Last nicht beachtet wurde ist es sinnvoll, einen gewissen Sicherheitsabstand zu den zulässigen Maximalwerten einzuhalten. Daher ist es empfehlenswert, die Chiptemperatur auf ca. 125°C zu beschränken. &lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist hier die parasitäre Diode im FET nicht berücksichtigt.&lt;br /&gt;
Wenn während der &amp;quot;off&amp;quot; Zeit ein Strom über die Diode fließt (Reverse recovery current oder Freilaufstrom), muß die dadurch &#039;&#039;&#039;zusätzlich&#039;&#039;&#039; entstehende Verlustleistung in die obige Berechnung der maximalen Chiptemperatur mit einfließen.&lt;br /&gt;
&lt;br /&gt;
==Treiberleistung==&lt;br /&gt;
&lt;br /&gt;
Auch wenn der MOSFET ein spannungsgesteuertes Bauelement ist, muss trotzdem bei jedem Einschalten und bei jedem Ausschalten die Gatekapazität umgeladen werden. Bei älteren Leistungs-FET - oder bei einem schlechten Design (!) - muss sogar teilweise mit negativer Spannung am Gate gearbeitet werden, um eine vollständige Sperrung zu erreichen.&lt;br /&gt;
Diese Umladung muss möglichst schnell erfolgen, um die Verluste im FET während der Umschaltphase zu minimieren. Dazu findet ein [[Mosfet-Übersicht#MOSFET-Treiber|MOSFET-Treiber]] Verwendung. Hier eine detaillierte Beschreibung zum [[Treiber]].&lt;br /&gt;
&lt;br /&gt;
Da die Gatekapazität nicht direkt im Datenblatt enthalten ist kann man sich mit der Eingangskapazität Ciss behelfen. Im Arbeitspunkt ist die Gatekapazität ungefähr 5x größer als der im Datenblatt für Ciss angegebene Wert. &lt;br /&gt;
Daher berechnet sich die Treiberleistung wie folgt: &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{Treiber} = C \cdot U^2 \cdot f = 5 \cdot C_\text{íss} \cdot U_\text{Gate}^2 \cdot f_\text{schalt}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1.Beispiel, kleine MOSFET-Steuerung mit niedriger Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 4{,}8\,\text{nF} \cdot 15\,\text{V}^2 \cdot 10\,\text{kHz} = 54\,\text{mW}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2.Beispiel, sehr große MOSFET-Steuerung für Induktionsheizung mit sehr hoher Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 24\,\text{nF} \cdot 15\,\text{V}^2 \cdot 250\,\text{kHz} = 6{,}75\,\text{W}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung, so ein MOSFET-Treiber hat auch einen Eigenverbrauch, der leicht zwischen 0,5 und 1 W liegen kann.&lt;br /&gt;
&lt;br /&gt;
Bei niedrigen PWM-Frequenzen kann man Logic Level MOSFETs auch direkt per CMOS-Ausgang ansteuern, z.B. mit einem [[AVR]], wie in diesem [http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag] zu sehen ist.&lt;br /&gt;
&lt;br /&gt;
== Low- und High-Side ==&lt;br /&gt;
&lt;br /&gt;
Definition LS- und HS:&lt;br /&gt;
 &lt;br /&gt;
;Low-Side Schalter: Der FET schaltet eine Last gegen GND - auch als LS-Schalter bezeichnet.&lt;br /&gt;
;High-Side Schalter: Der FET schaltet eine Last an die Versorgungsspannung – auch als HS-Schalter bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Anregungen oder Fragen auch gerne per Email an [http://www.mikrocontroller.net/user/show/powerfreak Powerfreak]. Dieser Artikel kann dadurch regelmäßig erweitert und ggf. durch ein FAQ ergänzt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== SOA Diagramm ==&lt;br /&gt;
&lt;br /&gt;
SOA-Diagramm (engl. &#039;&#039;&#039;S&#039;&#039;&#039;afe &#039;&#039;&#039;O&#039;&#039;&#039;perating &#039;&#039;&#039;A&#039;&#039;&#039;rea, sicherer Arbeitsbereich) beschreibt die zulässige Verlustleistung eines Transistors in Anhängigkeit des Drainstroms (I_D), der Drain-Source Spannung (U_DS) und der Pulsbreite. Als Beispiel sei hier der BUZ 11 genannt. Im nachfolgenden Diagramm ist das SOA-Diagramm dargestellt. Wie ist es zu verstehen? Zunächst gibt es eine Grenze auf der linken Seite, die schräge, dunkelblaue Line. Diese wird durch den minimalen R_DS_ON festgelegt, hier wirkt der MOSFET wie ein ohmscher Widerstand. Mehr Strom kann bei einer bestimmten Spannung nicht fließen. Die zweite Grenzlinie ist ganz rechts die pinkfarbene Linie, sie stellt die maximale Sperrspannung des MOSFET dar. Die dritte Grenze ist der maximal zulässige Drainstrom, hier im Beispiel 120A, dargestellt durch die gelbe Linie. Die maximale Spannung zwischen Drain und Source sowie der Drainstrom sind abhängig von der Pulsbreite, mit welcher der MOSFET betrieben wird. Bei nur 2,5µs Pulsbreite (Rechteckimpuls) müssen die beiden Parameter sich innerhalb der Fläche bewegen, welche durch die dunkelblaue, gelbe und die pinkfarbene Line begrenzt wird. Im Extremfall dürfen 50V anliegen und 120A fließen, das sind satte 6kW Pulsleistung! Werden die Pulse breiter, so sinken die zulässigen Ströme und Spannungen, bei 1ms (dunkelblaue Linie bis zur braunen Linie, dann zur pinkfarbenen Linie) sind maximal noch 50V und 7A zulässig, also nur noch 350W. Die letzte Linie stellt den Fall für Gleichstrom (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent), also Dauerbelastung dar, hier sind bei 50V maximal 1,5A zulässig, was einer Dauerverlustleistung von 75W entspricht. MOSFETs, welche nur für Schaltbetrieb und nicht für [[#Linearbetrieb von MOSFETs | Linearbetrieb]] geeignet sind, haben keine Kennlinie für DC. Im normalen Schaltbetrieb liegt der Arbeitspunkt auf der linken Grenzlinie R_DS_ON_MIN. Nur im Linearbetrieb liegt der Arbeitspunkt innerhalb der Fläche, welche durch die Außenlinien begrenzt wird.&lt;br /&gt;
&lt;br /&gt;
[[bild: SOA-BUZ11.png | thumb | 300px| SOA-Diagramm]]&lt;br /&gt;
&lt;br /&gt;
Bei der Anwendung des Diagramms gilt es einiges zu beachten. Die Pulsleistungen sind nur zulässig, wenn der MOSFET vorher kalt ist, sprich ca. 25°C Sperrschichttemperatur hat. War er vorher schon heiß, reduziert sich die zulässige Belastung deutlich. Ebenso dürfen die Pulse nicht zu schnell wiederholt werden, denn dann ist der MOSFET noch vom vorherigen Puls aufgeheizt. Im Fall von DC sind 75W Verlustleistung auch eher ein theoretischer Wert, welcher real nur schwer erreicht werden kann, wenn der MOSFET auf einem sehr großen [[Kühlkörper]] optimal montiert ist. Praktisch liegen die erreichbaren Werte eher bei der Hälfte.&lt;br /&gt;
&lt;br /&gt;
(Anm. Eigentlich müsste für die R_DS_ON Grenzlinie R = U / I der minimale R_DS_ON rauskommen, hier ~40mOhm, es kommen aber ~80mOhm raus. Die Ursache dafür ist unklar, möglicherweise liegt hier ein Sicherheitsfaktor zu grunde).&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Linearbetrieb von MOSFETs ==&lt;br /&gt;
&lt;br /&gt;
Der Großteil der Anwendungen nutzt MOSFETs als Schalter, d.h. der MOSFET ist entweder voll gesperrt oder voll durchgesteuert. Dafür gelten auch all die Hinweise in diesem Artikel. In bestimmten Anwendungen werden MOSFETs aber auch im Linearbetrieb eingesetzt, z.B in linearen Endstufen für Audio, Video, elektronischen Lasten und Stromquellen. Hier muss man einiges beachten. Ein verbreiteter Irrtum besteht darin zu glauben, MOSFETs könne man im Linearbetrieb einfach parallel schalten, weil der positive Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; eine Symmetrierung bewirkt, ähnlich den Emitterwiderständen bei parallelgeschalteten Bipolartransistoren. Das ist &#039;&#039;ausschließlich&#039;&#039; im Schaltbetrieb möglich, und daher falsch! Im Linearbetrieb spielt der Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; keine Rolle, weil der MOSFET selten bis nie komplett durchgesteuert ist. Eben darum ist beim Linearbetrieb der minimale &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; in den meisten Fällen unwichtig und man kann auch eher hochohmige, ältere MOSFETs verwenden, wie z.B. den BUZ11.&lt;br /&gt;
&lt;br /&gt;
Hier wirkt vielmehr der negative Temperaturkoeffizient (TK) der Thresholdspannung &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, vergleichbar dem negativen TK der Basis-Emitter-Spannung von Bipolartransistoren. D.h. mit steigender Temperatur und konstanter Gate-Source-Spannung steigt der Stromfluss der Drain-Source Strecke. In einer Parallelschaltung von MOSFETs würde dies bedeuten, dass der MOSFET mit dem geringfügig größeren Drainstrom (Fertigungstoleranzen) wärmer wird, was zu einem weiter steigenden Drainstrom und damit noch mehr Wärme führt. Damit ist die Schaltung thermisch instabil und würde zum Durchbrennen der MOSFETs führen, einer nach dem Anderen. &lt;br /&gt;
&lt;br /&gt;
Um das zu verhindern muss man relativ große Ausgleichswiderstände in die Source-Leitung der einzelnen MOSFETs schalten, um diese Drift zu kompensieren. Dadurch verschlechtert sich natürlich der Wirkungsgrad des Verstärkers. MOSFETs haben einen TK von typisch -5mV/K für &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, das ist mehr als das doppelte von Bipolartransistoren mit typisch -2mV/K, weshalb die Symmetrierungswiderstände mehr als doppelt so groß sein müssen. Weiterhin muss man beachten, dass die Toleranzen von &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt; sehr groß sind, im Bereich von Volt! Das kann man sinnvoll nicht mehr mit Gatewiderständen symmetrieren, hier muss man die MOSFETs ausmessen und Gruppen mit geringen Toleranzen in einer Schaltung verwenden (engl. matching). &lt;br /&gt;
&lt;br /&gt;
Eine andere Möglichkeit ist die getrennte Ansteuerung der einzelnen MOSFETs, das wird oft in elektronischen Lasten bzw. [[Konstantstromquelle#Konstantstromquelle mit Operationsverstärker und Transistor | Konstantstromquellen]] gemacht. Hier treten keine zusätzlichen Verluste auf und der Mehraufwand in der Ansteuerung ist meist unkritisch.&lt;br /&gt;
&lt;br /&gt;
Weiterhin muss man beachten, dass viele der heutigen HochleistungsMOSFETs intern eine Parallelschaltung vieler kleiner MOSFET-Zellen (z.B. sogenannte Trench-FET) sind, und somit oft für den Linearbetrieb ungeeignet sind. Denn auch dort können einzelne Zellen überhitzen und durchbrennen (Hot Spot). Ob ein MOSFET für den Linearbetrieb tauglich ist steht manchmal im Datenblatt, oft aber eher nicht, eben weil die meisten MOSFETs als Schalter entwickelt und gebaut sind. Typische Vertreter für Linearbetrieb findet man in der [[MOSFET-Übersicht]]. Ein wichtiges Indiz für Linearbetrieb ist eine Kurve für DC im [[#SOA_Diagramm | SOA-Diagramm]]. Meist geht es dort nur bis 10ms, DC fehlt, eben weil DC (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent = Gleichstrom = Linearbetrieb) nicht zulässig ist. Manchmal hat der Hersteller auch &amp;quot;vergessen&amp;quot;, die Kennlinie für DC mit reinzuschreiben, wie z.B. bei [http://www.irf.com/product-info/hi-rel/alerts/fv5-p-09-01-A.pdf IRF], wie in diesem [http://www.mikrocontroller.net/topic/291760#3106758 Beitrag] zu erfahren ist.&lt;br /&gt;
Ein recht gutes Indiz dafür, ob ein FET für den Linearbetrieb taugt, ist die Vorwärtssteilheit. Diese kennzeichnet die Abhängigkeit des Drainstromes von der Ansteuerung am Gate als &amp;lt;math&amp;gt;S = \Delta i_d/\Delta u_{gs}&amp;lt;/math&amp;gt;. Moderne Trench-FET erreichen heute Steilheiten im dreistelligen Bereich und sind für Linearanwendungen völlig unbrauchbar. Zum Vergleich: Der BUZ11 kommt mit gerade einmal 4 bis 5 Siemens daher.&lt;br /&gt;
&lt;br /&gt;
In diesem Beitrag wird die DC-Linie im SOA-Diagramm noch genauer erklärt: [http://www.mikrocontroller.net/topic/319961#3473567 Re: MOSFET Linearbetrieb möglich?]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
* [[Mosfet-Übersicht]]&lt;br /&gt;
* [[IGBT]]&lt;br /&gt;
* [[TRIAC]]&lt;br /&gt;
* [[Kühlkörper]] &lt;br /&gt;
* [[Zwischenkreiskapazität]]&lt;br /&gt;
* [[Treiber]]&lt;br /&gt;
* [[Snippets#Wie_schlie.C3.9Fe_ich_einen_MOSFET_an_einen_Mikrocontroller_an.3F|Wie schließe ich einen Mosfet an einen Mikrocontroller an?]]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/H-Br%C3%BCcken_%C3%9Cbersicht Übersicht H-Brücken]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/168218#1609684 Forumsbeitrag]: MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/186785#new Forumsbeitrag]: nochmal MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/319961#3473567 Forumsbeitrag]: sehr ausführlicher Forumsbeitrag über MOSFETs im Linearbetrieb. Berücksichtigt auch den Spirito-Effekt.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/143324#new Forumsbeitrag]: Über eine elektronische Last, sehr lang&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag]: Logic Level MOSFETs direkt mit einem [[AVR]] treiben.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787855 Forumsbeitrag]: MOSFETs im Linearbetrieb, Laborerfahrungen&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787945 Forumsbeitrag]: MOSFETs für Linearbetrieb&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/269642?goto=2820617#2820617 Forumsbeitrag]: Verpol- und Überspannungsschutz mit MOSFETs&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/425414#4981611 Forumsbeitrag]: Linearbetrieb von MOSFETs&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.elektronikinfo.de/strom/feldeffekttransistoren.htm Feldeffekttransistoren bei elektronikinfo.de]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0207011.htm FET im ELKO]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0510161.htm MOSFET im ELKO]&lt;br /&gt;
* [http://www.sprut.de/electronic/switch/nkanal/nkanal.html MOSFET bei sprut.de]&lt;br /&gt;
* [http://sound.westhost.com/articles/hexfet.htm#51 MOSFETs in Audioendstufen, engl.]&lt;br /&gt;
* [http://irf.custhelp.com/cgi-bin/irf.cfg/php/enduser/std_adp.php?p_faqid=214&amp;amp;p_created=1019728945&amp;amp;p_sid=pt9ITiCj&amp;amp;p_accessibility=0&amp;amp;p_redirect=&amp;amp;p_lva=&amp;amp;p_sp=cF9zcmNoPTEmcF9zb3J0X2J5PSZwX2dyaWRzb3J0PSZwX3Jvd19jbnQ9MTQsMTQmcF9wcm9kcz0mcF9jYXRzPSZwX3B2PSZwX2N2PSZwX3BhZ2U9MSZwX3NlYXJjaF90ZXh0PWxpbmVhcg**&amp;amp;p_li=&amp;amp;p_topview=1 FAQ Answer ID 214 bei IRF zum Linearbetrieb]&lt;br /&gt;
* [http://www.nxp.com/documents/application_note/AN11158.pdf AN11158 - Understanding power MOSFET data sheet parameters] von NXP (PDF)&lt;br /&gt;
* [https://assets.nexperia.com/documents/technical-note/TN00008.pdf TN00008 - Power MOSFET frequently asked questions and answers] von nexperia (PDF)&lt;br /&gt;
* [http://www.infineon.com/dgdl/Infineon+-+Application+Note+-+PowerMOSFETs+-+OptiMOS%E2%84%A2+-+Linear+Mode+Operation+and+SOA+Power+MOSFETs.pdf?fileId=db3a30433e30e4bf013e3646e9381200 AN: Linear Mode Operation andSafe Operating Diagram of Power-MOSFETs] von Infineon (PDF)&lt;br /&gt;
* [http://www.ixys.com/Documents/Articles/Article_Linear_Power_MOSFETs.pdf MOSFETs Withstand Stress of Linear-Mode Operation] Neuentwickelte MOSFETs für Linearbetrieb (PDF)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]] [[Kategorie:Leistungselektronik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107026</id>
		<title>FET</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107026"/>
		<updated>2024-07-19T16:42:18Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* FET-Typen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel versteht sich als Unterpunkt zum Artikel [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein FET (engl. &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor) ist ein  Feldeffekttransistor. Der FET ist ein Bauelement, das im Gegensatz zum Bipolartransistor (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT) mit Spannung und nicht mit Strom gesteuert wird. Unterschieden werden&lt;br /&gt;
* MOSFET = engl. &#039;&#039;&#039;M&#039;&#039;&#039;etall &#039;&#039;&#039;O&#039;&#039;&#039;xide &#039;&#039;&#039;S&#039;&#039;&#039;emiconductor &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor; Metalloxidschicht-FET, größte Teilgruppe der FETs mit isoliertem Gate &lt;br /&gt;
* JFET = engl. &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, Übergangszonen FET, der steuerbare Kanal wird durch einen PN-Übergang wie in einer Diode gebildet&lt;br /&gt;
&lt;br /&gt;
Die drei Anschlüsse eines FETs werden &#039;&#039;Gate&#039;&#039;, &#039;&#039;Drain&#039;&#039; und &#039;&#039;Source&#039;&#039; genannt. Unter Umständen ist ein vierter Anschluß vorhanden, der &#039;&#039;Bulk&#039;&#039; genannt wird. Normalerweise ist Bulk intern mit Source verbunden. Wenn dies nicht der Fall ist, muss diese Verbindung durch den Designer in der Schaltung hergestellt werden.&lt;br /&gt;
&lt;br /&gt;
== FET-Typen ==&lt;br /&gt;
&lt;br /&gt;
FETs werden hauptsächlich unterschieden in N-Kanal und P-Kanal, sowie &amp;quot;selbst sperrend = Anreicherungstyp&amp;quot; (engl. enhancement type) und &amp;quot;selbst leitend = Verarmungstyp&amp;quot; (engl. depletion type). Beim selbstleitenden FET ist der Transistor bei 0V Gate-Source Spannung maximal leitend (durchgesteuert) und wird durch Anlegen einer Spannung ans Gate gesperrt. Beim selbstsperrenden FET (größte Gruppe) ist der Transistor bei 0V Gate-Source Spannung gesperrt und wird durch Anlegen einer Spannung ans Gate leitend. Ist die Linie zwischen Drain und Source durchgezogen handelt es sich um einen selbstleitenden, bei einer gestrichelten Linie um einen selbstsperrenden FET. JFETs gibt es nur als Verarmungstyp.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Artikel wird nur der &#039;&#039;&#039;selbstsperrende&#039;&#039;&#039; MOSFET betrachtet, weil nur dieser einen althergebrachten Bipolartransistor „ersetzen“ kann.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Typen von Feldeffekttransistoren&amp;lt;br/&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Typ &lt;br /&gt;
! N-Kanal &lt;br /&gt;
! P-Kanal&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| JFET&amp;lt;br/&amp;gt;SFET&amp;lt;br/&amp;gt;(selbst leitend) || [[bild:JFET-N.png|center]]&lt;br /&gt;
* drittgrößte Gruppe&lt;br /&gt;
* bislang nur für kleine Leistungen verfügbar&lt;br /&gt;
* JFETs mit hoher Leistung sind im Kommen&lt;br /&gt;
* Eingangsstufen von OPVs&lt;br /&gt;
* Eingangsstufen von HF-Verstärkern bis in den GHz-Bereich&lt;br /&gt;
* als einfache [[Konstantstromquelle]] geeignet&lt;br /&gt;
| [[bild:JFET-P.png|center]]&lt;br /&gt;
* selten&lt;br /&gt;
* ersatzhalber für „fehlenden“ P-Kanal-Verarmungs-MOSFET&lt;br /&gt;
* Ältester Vertreter der Unipolartransistoren&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Anreicherungstyp&amp;lt;br/&amp;gt;(selbst sperrend)&amp;lt;br/&amp;gt;&#039;&#039;Enhancement Mode&#039;&#039; || [[bild:MOS-EN.png|center]]&lt;br /&gt;
* Mit Abstand größte Gruppe; wenn nicht besonders erwähnt ist &#039;&#039;dieser&#039;&#039; gemeint&lt;br /&gt;
* Eingangsstufen von HF-Verstärkern bis in den GHz-Bereich&lt;br /&gt;
** Typen mit zwei Gates zum Regeln und Mischen üblich&lt;br /&gt;
* Auch als Doppel-MOSFET oder zusammen mit P-Kanal-MOSFET in einem Gehäuse gängig&lt;br /&gt;
| [[bild:MOS-EP.png|center]]&lt;br /&gt;
* zweitgrößte Gruppe&lt;br /&gt;
* bei gleicher Geometrie etwas schlechter als ein N-Kanal-Typ, da „Löcherleitung“ (aus „Defektelektronen“ = gedachte positive Ladungsträger) vorliegt&lt;br /&gt;
* Auch als Doppel-MOSFET oder zusammen mit N-Kanal-MOSFET in einem Gehäuse gängig&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Verarmungstyp&amp;lt;br/&amp;gt;(selbst leitend)&amp;lt;br/&amp;gt;&#039;&#039;Depletion Mode&#039;&#039; || [[bild:MOS-DN.png|center]]&lt;br /&gt;
* selten aber käuflich&lt;br /&gt;
* Sinnvolle Anwendungen ergeben sich vor allem in Reihenschaltungen&lt;br /&gt;
| [[bild:MOS-DP.png|center]]&lt;br /&gt;
* Nicht in freier Wildbahn gesichtet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Vorteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Niedrigere Verluste als bei Bipolartransistoren.&lt;br /&gt;
* Sehr schnelles Schalten möglich, daher für sehr hohe Frequenzen geeignet (keine Speicherzeit wie beim BJT).&lt;br /&gt;
* Einfaches Parallelschalten im Schaltbetrieb, da Unterschiede im &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; durch den positiven Temperaturkoeffizienten ausgeglichen werden.&lt;br /&gt;
* Leistungslose Ansteuerung im statischen Fall, jedoch hohe Umladeverluste am Gate!&lt;br /&gt;
* oft preiswerter als vergleichbare Bipolartransistoren (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT)&lt;br /&gt;
* Relativ unempfindlich gegen Überspannung zwischen Drain und Source. Bei Überschreitung der Maximalspannung zwischen Drain und Source findet ein sogenannter &amp;quot;Durchbruch&amp;quot; statt. Dies ist vergleichbar mit dem Zener-Effekt. Ist die Energiemenge begrenzt, ist dieser Durchbruch reversibel und der FET wird NICHT zerstört. Beim Bipolartransistor ist die dafür zulässige Energiemenge viel kleiner, weil die den Strom durchquerende Fläche zu Hotspots neigt.&lt;br /&gt;
&lt;br /&gt;
=== Nachteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Nur bedingt für hohe Spannungen [[Transistor#Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein ? |geeignet]], die ON-Verluste sind ab ca. 250V höher als bei einem [[IGBT]]. &lt;br /&gt;
* Parasitäre Diode parallel zur Drain-Source Strecke ist immer enthalten, das (Ab-)Schaltverhalten dieser Dioden ist meist schlechter als separate Dioden, was häufig zu unerwünschten Schwingungen führt.&lt;br /&gt;
* Empfindlicher gegen ESD am Gate als BJT&lt;br /&gt;
* Positiver Temperaturkoeffizient (TK), der &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; ist stark temperaturabhängig und steigt von 25°C (Datenblattangabe) auf 150°C ungefähr um den Faktor 2. Dadurch steigen auch die Verluste und damit die Erwärmung des Bauteiles.&lt;br /&gt;
&lt;br /&gt;
=== Erklärung der wichtigsten Datenblattwerte ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:7em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | Beispiel&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Drain Source Voltage &amp;lt;br&amp;gt;(Breakdown) || V(BR)_DSS &amp;lt;br&amp;gt; V_DS || 75 V || Maximale Spannungsfestigkeit des Bauteiles zwischen Drain und Source&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Drain Current  || I_D(on)   || 55 A @ 125 °C  || Maximaler Dauerstrom bei 125°C Gehäusetemperatur &lt;br /&gt;
|-&lt;br /&gt;
| Pulsed Drain Current || ID_pulse &amp;lt;br&amp;gt; I_CD(on) || 240 A || Maximaler Pulsstrom (Achtung die zulässige Zeitdauer des Pulses kann nur über die maximale Junctiontemperatur ermittelt werden)&lt;br /&gt;
|-&lt;br /&gt;
| Gate Charge || Q&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; ||  320 nC || Gate-Ladung in Coloumb oder Amperesekunden, die zum Ein- oder Ausschalten des MOSFET erforderlich ist. Dies berücksichtigt die Nichtlinearität der Kapazität und (richtig?) den Miller-Effekt. Mit dieser Angabe wird der für das Schalten von Leistungs-MOSFET benötigte Gatestrom für eine bestimmte Schaltzeit ermittelt. Dieser liegt üblicherweise im einstelligen Ampere-Bereich. Moderne Typen mit extra geringer Gate-Ladung können so schneller schalten.&lt;br /&gt;
|-&lt;br /&gt;
| Repetetive Avalanche Energy || t_sc ||  280 mJ || Maximale Energie, welche beim Avalanche Durchbruch bei Überschreiten der maximalen Drain-Source Spannung im MOSFET bei z.&amp;amp;nbsp;B. 1% Puls/Pausen Verhältnis regelmäßig auftreten darf, ohne den FET zu schädigen&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_ON&amp;lt;/sub&amp;gt; ||  0,01 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;25°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_on&amp;lt;/sub&amp;gt; ||  0,021 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;175°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Thermal Resistance&amp;lt;br&amp;gt;(junction-case) ||  R&amp;lt;sub&amp;gt;th_JC&amp;lt;/sub&amp;gt; ||  0,8 K/W || Thermischer Widerstand im Transistor vom der „Sperrschicht“ (junction eines Bipolartransistors, ja altmodischer Index, hier besser „Kanal“) bis zur Rückseite oder bestmöglichen Kühlmöglichkeit des Transistorgehäuses (case)&lt;br /&gt;
|-&lt;br /&gt;
| Gate-Source&amp;lt;br&amp;gt;Threshold Voltage ||  V_GS(th) || 2,0-4,5 V || Gatespannung, ab welcher der Transistor minimal leitend wird (I_D typisch 100-200µA), große Toleranz, typisch 1:2 zwischen Minimum und Maximum&lt;br /&gt;
|-&lt;br /&gt;
| Turn-on Delay ||  t_d(on) || 40 ns ||  Verzögerung zwischen dem Einschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Rise Time ||  t_r || 200 ns ||  Anstiegszeit des Transistorstromes am Drain&lt;br /&gt;
|-&lt;br /&gt;
| Turn-off Delay || t_d(off) || 120 ns ||  Verzögerung zwischen Abschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Fall Time ||  t_f  || 60 ns || Abfallzeit des Transistorstromes am Drain  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die oben genannten Zeiten gelten ausschließlich unter den angegebenen Messbedingungen (Gatewiderstand, Treiberspannung, sowie einer &#039;&#039;&#039;FET-Teperatur von 25°C!&#039;&#039;&#039;) und müssen für die eigene Anwendung ggf. neu berechnet werden. Meist wird man sie eher messen, weil die Rechung zu aufwändig und bisweilen unmöglich ist. &lt;br /&gt;
&lt;br /&gt;
==== Gate-Source Threshold Voltage ====&lt;br /&gt;
Gerade bei der &#039;&#039;&#039;Gate-Source Threshold Voltage &amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt;&#039;&#039;&#039; gibt es hier immer wieder Verwirrung. Sie gibt an, ab welcher Spannung der MOSFET anfängt, minimal leitfähig zu werden. Diese Spannung ist technologisch bedingt auch heute noch einer starken Toleranz unterworfen, typischerweise hat der Bereich  bei dem der FET zu leiten beginnt eine Spreizung von etwa 1:2 zwischen Minimum und Maximum. Welche Spannung man nun wirklich anlegen muss, um den gewünschten &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt; zu erreichen kann der Tabelle im jeweiligen Datenblatt entnommen werden. Dabei unbedingt die angegebene Gatespannung beachten, nur dieser Wert ist garantiert!.&lt;br /&gt;
&lt;br /&gt;
[[bild: IRLZ34N_R_DS_ON.png | thumb | 800px | R_DS_ON im Datenblatt des IRLZ34N]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Die Kurvenschar von &amp;lt;math&amp;gt;I_D&amp;lt;/math&amp;gt; über &amp;lt;math&amp;gt;U_{DS}&amp;lt;/math&amp;gt; in Abhängigkeit von &amp;lt;math&amp;gt;U_{GS}&amp;lt;/math&amp;gt; stellt immer nur typische Werte dar, keine garantierten Extremwerte (engl. worst case). Als Standardwerte kann man 10-15V für einen Standardtypen und ca. 3-5V für einen Logic Level MOSFET (LL-FET) ansetzen. Kleinsignal-FETs leiten schon ab ca 1V. Bei Ansteuerung mit 5V benötigt man also einen Typen, der &#039;&#039;&#039;sicher&#039;&#039;&#039; bei 5V voll durchgesteuert ist, z.B. IRLZ34N. Bei 3,3V ist er bereits nicht mehr zuverlässig nutzbar. Es gibt auch Typen mit noch geringerer Spannung für Vollaussteuerung. Wer einen BUZ11 (&amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt; 4V max.) mit 5V ansteuert riskiert ein Abfackeln des MOSFETs, denn je nach Toleranz kann er bereit ganz gut aufgesteuert sein oder auch nicht. Das soll in den nachfolgenden Diagrammen dargestellt werden. Zunächst die Transferkennlinie. Sie gibt an, wieviel Drainstrom in Abhängigkeit der Gatespannung fließen kann, wobei die Drainspannung konstant ist, hier im Beispiel 25V. Ein typischer BUZ11 (mittlere, schwarze Kurve) fängt bei 3V zu leiten an und erreicht bei 6V am Gate ca. 17A. Erwischt man nun ein kritisches Exemplar, das erst bei 4V zu leiten anfängt (rechte, rote Kurve um 1V parallel verschoben), so kann dieser bei 6V nur 8A leiten, für 17A braucht er 7V. Der günstige Fall, daß der BUZ11 schon bei 2V anfängt zu leiten sieht man in der linken, roten Kurve.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UGS_ID.png | thumb | 800px | Transferkennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Wenn man die drei Fälle (min, typ, max) mit 5V ansteuert, erhält man die Arbeitspunkte 1-3 mit einem maximal schaltbaren Drainstrom von 2,5, 8 und 17A. Diese kann man in das Ausgangskennlinienfeld übertragen. Wir nehmen max. 1V U_DS an. Es ergeben sie die gleichen Ströme. Der maximale Drainstrom weicht um -5,5/+9A vom typischen Fall ab. Erhöht man nun die Gate-Source Spannung auf 10V (Arbeitspunkt 4), schwankt der maximal schaltbare Drainstrom nur noch um -2/+2A, außerdem liegt er mit 30A deutlich höher. Der BUZ11 ist somit sicher durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UDS_ID_UGS.png | thumb | 800px | Ausgangskennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
==== Parasitäre Diode ====&lt;br /&gt;
&lt;br /&gt;
Der Schwerpunkt in der FET-Entwicklung liegt auf den geringst-möglichen &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt;,  die Diode entsteht auf Grund des Herstellungsprozesses, und wird nur nachrangig verbessert, da viele Optimierungsversuche auch einen Einfluss auf wichtige Kennwerte des FETs hatten und haben.&lt;br /&gt;
Daher muss sorgfältig geprüft werden, ob die Schaltgeschwindigkeit, die Recovery-Time und die damit verbundenen Verluste sowie die dabei erzeugte unerwünschte EMV-Abstahlung tolerierbar ist, oder nicht.&lt;br /&gt;
Hier hilft es oft eine optimierte Diode / Schottky-Diode zum FET parallel zu schalten. Ganz ausblenden läßt sich die parasitäre Diode jedoch nicht, jedoch kann man den Anteil des Stromes beeinflussen, der über die intere Diode fliest.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039; Parasitäre Diode des FETs  &#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Current  ||  I_S || 75A || Maximaler Dauerstrom der parasitären Diode, meist identisch zum maximalen Dauerstrom des MOSFETs&lt;br /&gt;
|-&lt;br /&gt;
| Forward Voltage ||  V_SD || 1,3V || Spannungsfall an der parasitären Diode &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Time ||  t_rr || 120ns || Zeit, welche die Elektronen brauchen um aus der leitenden Diode vollständig abzufließen. Während dieser Zeit fließt der Strom in &#039;&#039;&#039;Rückwärtsrichtung&#039;&#039;&#039; durch die Diode und erzeugt relativ viel Verlustleistung. &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Charge ||  Q_rr || 60nC || Ladungsmenge, die während t_rr rückwärts durch die Diode fließt.  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Haupttypen und Gatespannungslevel ==&lt;br /&gt;
&lt;br /&gt;
===Unterschied N-Kanal / P-Kanal FET===&lt;br /&gt;
&lt;br /&gt;
Im Schaltsymbol werden die MOSFET-Typen meist durch die Pfeilrichtung in der Mitte des Symbols (eigentlich &amp;quot;Bulk&amp;quot;) vom oder zum Gate unterschieden.  Zeigt der Pfeil zum Gate hin, handelt es sich um einen N-Kanal-FET, zeigt der Pfeil vom Gate weg um einen P-Kanal FET.&lt;br /&gt;
&lt;br /&gt;
Der große Vorteil des N-Kanal FETs (Elektronenleitung) ist, daß er immer niederohmiger ist, als ein gleich großer P-Kanal FET (Löcherleitung). Daher sind P-Kanal Typen bei vergleichbaren Werten auch immer größer = teuerer da weniger Chips auf einem Wafer Platz haben.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET muss die Gatespannung positiv gegenüber Source sein. Dabei wird der FET dann leitend, wenn die sogenannte &amp;quot;threshold voltage&amp;quot; (Schwellenspannung) erreicht wird. Eine typische Anwendung ist z.&amp;amp;nbsp;B. ein &#039;&#039;&#039;Low-Side Schalter&#039;&#039;&#039;: Source an GND, Drain an die Last, Ansteuerung des N-Kanal FETs mit 12V gleichbedeutend mit 12V ÜBER den Source = GND Potential.&lt;br /&gt;
 &lt;br /&gt;
Beim P-Kanal FET als HS-Schalter muss die Gatespannung negativer=niedriger als das Sourcepotential sein.Beispiel.&lt;br /&gt;
Beispiel:  &lt;br /&gt;
Lastspannung = 400V d.h. Source an 400V, Last zwischen Drain und GND, Ansteuerung des P-Kanal FETs mit 388V, also 12V UNTER dem Sourcepotential.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET als HS-Schalter muss die Gatespannung positver=höher als das Sourcepotential sein.&lt;br /&gt;
Beispiel:&lt;br /&gt;
Lastspannung = 400V d.h. Drain an 400V, die Last zwischen Source und GND, Ansteuerung des N-Kanal FETs mit 412V, also 12V ÜBER dem Sourcepotential.&lt;br /&gt;
In diesem Fall ist aber eine zusätzliche Spannungsquelle erforderlich, denn der FET wird mit einer Spannung über der Lastspannung eingeschaltet. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weblinks&#039;&#039;&#039;&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-1-of-2/?utm_source=eetimes&amp;amp;utm_medium=networksearch A primer on high-side FET load switches (Part 1 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products, Micrel, Inc., 5/3/2007 4:14 PM EDT, www.eetimes.com&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-2-of-2/ A primer on high-side FET load switches (Part 2 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products. Micrel, Inc., 5/7/2007 1:36 PM EDT, www.eetimes.com&lt;br /&gt;
* [http://www.vishay.com/docs/70611/70611.pdf AN804 P-Channel MOSFETs, the Best Choice for High-Side Switching (PDF)] von Vishay Siliconix&lt;br /&gt;
&lt;br /&gt;
===Unterschied Logic-Level / &amp;quot;Normal&amp;quot;-Level===&lt;br /&gt;
&lt;br /&gt;
Den meisten FETs ist gemein, daß sie mit einer Spannung von 10..15V angesteuert werden müssen, um den minimalen Einschaltwiderstand zu erreichen. Diese FETs lassen sich nicht ohne weiteres mit einem CMOS-Pegel von 5V ansteuern. Es gibt jedoch für diesen Anwendungsfall sogenannte &amp;quot;Logic Level&amp;quot; (LL) FETs, die schon bei einer Gatespannung von etwa 4,5V voll durchgesteuert sind. Einige Kleinsignal-FETs sind schon ab ca. 1,2V voll durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
== Beispiel zur Bauteiledimensionierung ==&lt;br /&gt;
&lt;br /&gt;
=== Spannungsfestigkeit ===&lt;br /&gt;
&lt;br /&gt;
Die höchste vorkommende Betriebsspannung + Abschaltüberspannung soll kleiner als ca. 80% der Spannungsfestigkeit des Bauteiles sein. &lt;br /&gt;
&lt;br /&gt;
Achtung: Zwischen dem je nach Anwendungsfall erforderlichen Pufferkondensator und dem FET wird es immer eine parasitäre Induktivität geben.&lt;br /&gt;
Abhängig von Schaltgeschwindigkeit und Induktivität wird im Schaltmoment eine mehr oder weniger große Übrspannungsspitze produziert. Dieser Peak&lt;br /&gt;
addiert sich auf die aktuelle Versorgungsspannung.&lt;br /&gt;
&lt;br /&gt;
Überschlagsrechnung als Beispiel:&lt;br /&gt;
* Schaltgeschwindigkeit:  dI/dt = -100A/µs (= Abschalten von 5A innerhalb 50ns),&lt;br /&gt;
* Induktivität:   L = 1µH (~ 1 m loses, ungebündeltes Kabel)&lt;br /&gt;
* dU=-L*dI/dt = -1µH * (-100A / 1µs) = 100V&lt;br /&gt;
&lt;br /&gt;
Dies bedeutet, daß an der &amp;quot;Induktivität&amp;quot; zwischen Transistor und Kondensator - Aufgrund von Selbstinduktion im Schaltmoment - ein Überspannungspuls von ca. 100V entsteht, der auf die Betriebsspannung aufzuschlagen ist, also lieber die Leitung kürzer machen, und - sofern möglich - nicht ganz so schnell schalten.&lt;br /&gt;
&lt;br /&gt;
=== Stromtragfähigkeit ===&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt ist eine Stromtragfähigkeit bei 25°C, und meist noch bei einer höheren Temperatur z.B. 125°C, 150°C oder 175°C Kühlfahnentemperatur angegeben. Dieser Wert ist als ERSTE Entscheidungsgrundlage ausreichend, aber aus der theoretisch abführbaren Verlustleistung errechnet, und&lt;br /&gt;
* dient zum qualitativen Vergleich von Transistoren bezüglich ihres R_ds(on) und ihres Wärmewiderstands.&lt;br /&gt;
* ist für die Dimensionierung einer Schaltung nur als Richtwert zu interpretieren. &lt;br /&gt;
* ist ohne Schaltverluste genannt, und daher nur für einen Schaltbetrieb von wenigen Hz gültig. Außerdem wird ein annähernd idealer Kühlkörper unterstellt, der trotz der Verlustleistung das Gehäuse des Transistors auf der angegebenen Temperatur halten kann.&lt;br /&gt;
* entbindet einen nicht davon den Kopf einzuschalten... siehe die nachfolgenden Zeilen.&lt;br /&gt;
* Liegt der Strom für den die Schaltung entwickelt wird mit ca. 10..20% Abstand unter dem Datenblattwert von 125°C ist dieses Bauteil vermutlich verwendbar (siehe Detailberechnungen unten !).   &lt;br /&gt;
* Ist der benötigte Strom im Bereich oder größer als der zulässige bei 125°C sollte entweder ein anderer Typ eingesetzt oder mehrere FETs &#039;&#039;des gleichen Typs&#039;&#039; parallelgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
== Verlustleistung ==&lt;br /&gt;
&lt;br /&gt;
Hier wird eine Näherung für eine getaktete Anwendung betrachtet. In einem Transistor treten sowohl beim Ein- und Ausschalten, als auch während der Einschaltphase Verluste im Bauteil auf. Diese Verluste führen zu einer Bauteilerwärmung. Die dabei entstehende Temperatur darf die maximal zulässige Bauteiletemperatur nie überschreiten. Bei den ersten Projekten ist zu empfehlen eine berechnete Chiptemperatur von ca. 125°C nicht zu überschreiten. Fast alle aktuell verfügbaren FETs nennen im Datenblatt eine Temperatur von 175°C als ihre maximale Chiptemperatur.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:32em&amp;quot; &lt;br /&gt;
|+ &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Symbol&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Wert&lt;br /&gt;
|-&lt;br /&gt;
| Betriebsspannung || U&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 70 V&lt;br /&gt;
|-&lt;br /&gt;
| Nennstrom || I&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 30 A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source Widerstand bei&amp;lt;br&amp;gt;Chiptemperatur: 125°C&amp;lt;br&amp;gt; Gatespannung: 10V || R&amp;lt;sub&amp;gt;DS&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt;&amp;lt;/sub&amp;gt; || 17 mΩ&lt;br /&gt;
|-&lt;br /&gt;
| Pulsbreite || t&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt; || 150 µs&lt;br /&gt;
|-&lt;br /&gt;
| Schaltfrequenz || ƒ&amp;lt;sub&amp;gt;schalt&amp;lt;/sub&amp;gt; || 5 kHz&amp;lt;br&amp;gt;T = 200µs&lt;br /&gt;
|-&lt;br /&gt;
| Einschaltzeit (risetime) || t&amp;lt;sub&amp;gt;r&amp;lt;/sub&amp;gt; || 500 ns&lt;br /&gt;
|-&lt;br /&gt;
| Ausschaltzeit (falltime) || t&amp;lt;sub&amp;gt;ƒ&amp;lt;/sub&amp;gt; || 800 ns &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Leitend-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Während der FET bei [[PWM]]-Ansteuerung eingeschaltet ist, erzeugt er Verlustleistung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
P_\text{ON}&lt;br /&gt;
 = {I_\mathrm{N}}^2 \cdot R_\mathrm{DS_\mathrm{ON}} \cdot \frac{t_\mathrm{ON}}{T}&lt;br /&gt;
 = 30^2A^2 \cdot 17m\Omega  \cdot \frac{150\mu s}{200\mu s} = 11{,}5W&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schalt-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Vereinfachter Ansatz.&lt;br /&gt;
&lt;br /&gt;
Einschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_r}&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_r}{T} \\&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot 70V \cdot 30A \cdot \frac{500ns}{200\mu s}=1{,}3W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_f} &lt;br /&gt;
&amp;amp;=\tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_f}{T}\\&lt;br /&gt;
&amp;amp;=\tfrac14 \cdot 70V \cdot 30A \cdot \frac{800ns}{200\mu s}=2{,}1W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ und genauer kann man rechnen, wenn die Ein- Ausschaltenergie im Datenblatt angegeben ist. Aber Achtung! Die  Randbedingungen unter denen die genannte Energie ermittelt wurde, müssen genau so zutreffen.&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_f} = f_{schalt} \cdot E_{ON}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_r} = f_{schalt} \cdot E_{OFF}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Gesamtverlustleistung beträgt also in etwa 15W.&lt;br /&gt;
&lt;br /&gt;
Damit muß ein entsprechender [[Kühlkörper]] ausgelegt und die Chiptemperatur berechnet werden. z.&amp;amp;nbsp;B.:&lt;br /&gt;
* Kühlkörper mit einem R_th von 0,2K/W&lt;br /&gt;
* max. Umgebungstemperatur +60°C&lt;br /&gt;
* R_th &amp;quot;junction-case&amp;quot; des FETs 0,8K/W&lt;br /&gt;
* R_th der Wärmeleitfolie zwischen FET und Kühlkörper ca. 2,0K/W&lt;br /&gt;
* R_th gesamt: 3,0K/W &amp;lt;br&amp;gt;&lt;br /&gt;
* Bei einer Verlustleistung von 18W und einer Umgebungstemperatur von 60°C hat der Chip eine Temperatur von ca. 18W * 3,0K/W +60°C = 114°C. ==&amp;gt; o.k.!&lt;br /&gt;
&lt;br /&gt;
Unter Berücksichtigung der Tatsache, daß hier viele Vereinfachungen vorgenommen, und die Art der Last nicht beachtet wurde ist es sinnvoll, einen gewissen Sicherheitsabstand zu den zulässigen Maximalwerten einzuhalten. Daher ist es empfehlenswert, die Chiptemperatur auf ca. 125°C zu beschränken. &lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist hier die parasitäre Diode im FET nicht berücksichtigt.&lt;br /&gt;
Wenn während der &amp;quot;off&amp;quot; Zeit ein Strom über die Diode fließt (Reverse recovery current oder Freilaufstrom), muß die dadurch &#039;&#039;&#039;zusätzlich&#039;&#039;&#039; entstehende Verlustleistung in die obige Berechnung der maximalen Chiptemperatur mit einfließen.&lt;br /&gt;
&lt;br /&gt;
==Treiberleistung==&lt;br /&gt;
&lt;br /&gt;
Auch wenn der MOSFET ein spannungsgesteuertes Bauelement ist, muss trotzdem bei jedem Einschalten und bei jedem Ausschalten die Gatekapazität umgeladen werden. Bei älteren Leistungs-FET - oder bei einem schlechten Design (!) - muss sogar teilweise mit negativer Spannung am Gate gearbeitet werden, um eine vollständige Sperrung zu erreichen.&lt;br /&gt;
Diese Umladung muss möglichst schnell erfolgen, um die Verluste im FET während der Umschaltphase zu minimieren. Dazu findet ein [[Mosfet-Übersicht#MOSFET-Treiber|MOSFET-Treiber]] Verwendung. Hier eine detaillierte Beschreibung zum [[Treiber]].&lt;br /&gt;
&lt;br /&gt;
Da die Gatekapazität nicht direkt im Datenblatt enthalten ist kann man sich mit der Eingangskapazität Ciss behelfen. Im Arbeitspunkt ist die Gatekapazität ungefähr 5x größer als der im Datenblatt für Ciss angegebene Wert. &lt;br /&gt;
Daher berechnet sich die Treiberleistung wie folgt: &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{Treiber} = C \cdot U^2 \cdot f = 5 \cdot C_\text{íss} \cdot U_\text{Gate}^2 \cdot f_\text{schalt}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1.Beispiel, kleine MOSFET-Steuerung mit niedriger Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 4{,}8\,\text{nF} \cdot 15\,\text{V}^2 \cdot 10\,\text{kHz} = 54\,\text{mW}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2.Beispiel, sehr große MOSFET-Steuerung für Induktionsheizung mit sehr hoher Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 24\,\text{nF} \cdot 15\,\text{V}^2 \cdot 250\,\text{kHz} = 6{,}75\,\text{W}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung, so ein MOSFET-Treiber hat auch einen Eigenverbrauch, der leicht zwischen 0,5 und 1 W liegen kann.&lt;br /&gt;
&lt;br /&gt;
Bei niedrigen PWM-Frequenzen kann man Logic Level MOSFETs auch direkt per CMOS-Ausgang ansteuern, z.B. mit einem [[AVR]], wie in diesem [http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag] zu sehen ist.&lt;br /&gt;
&lt;br /&gt;
== Low- und High-Side ==&lt;br /&gt;
&lt;br /&gt;
Definition LS- und HS:&lt;br /&gt;
 &lt;br /&gt;
;Low-Side Schalter: Der FET schaltet eine Last gegen GND - auch als LS-Schalter bezeichnet.&lt;br /&gt;
;High-Side Schalter: Der FET schaltet eine Last an die Versorgungsspannung – auch als HS-Schalter bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Anregungen oder Fragen auch gerne per Email an [http://www.mikrocontroller.net/user/show/powerfreak Powerfreak]. Dieser Artikel kann dadurch regelmäßig erweitert und ggf. durch ein FAQ ergänzt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== SOA Diagramm ==&lt;br /&gt;
&lt;br /&gt;
SOA-Diagramm (engl. &#039;&#039;&#039;S&#039;&#039;&#039;afe &#039;&#039;&#039;O&#039;&#039;&#039;perating &#039;&#039;&#039;A&#039;&#039;&#039;rea, sicherer Arbeitsbereich) beschreibt die zulässige Verlustleistung eines Transistors in Anhängigkeit des Drainstroms (I_D), der Drain-Source Spannung (U_DS) und der Pulsbreite. Als Beispiel sei hier der BUZ 11 genannt. Im nachfolgenden Diagramm ist das SOA-Diagramm dargestellt. Wie ist es zu verstehen? Zunächst gibt es eine Grenze auf der linken Seite, die schräge, dunkelblaue Line. Diese wird durch den minimalen R_DS_ON festgelegt, hier wirkt der MOSFET wie ein ohmscher Widerstand. Mehr Strom kann bei einer bestimmten Spannung nicht fließen. Die zweite Grenzlinie ist ganz rechts die pinkfarbene Linie, sie stellt die maximale Sperrspannung des MOSFET dar. Die dritte Grenze ist der maximal zulässige Drainstrom, hier im Beispiel 120A, dargestellt durch die gelbe Linie. Die maximale Spannung zwischen Drain und Source sowie der Drainstrom sind abhängig von der Pulsbreite, mit welcher der MOSFET betrieben wird. Bei nur 2,5µs Pulsbreite (Rechteckimpuls) müssen die beiden Parameter sich innerhalb der Fläche bewegen, welche durch die dunkelblaue, gelbe und die pinkfarbene Line begrenzt wird. Im Extremfall dürfen 50V anliegen und 120A fließen, das sind satte 6kW Pulsleistung! Werden die Pulse breiter, so sinken die zulässigen Ströme und Spannungen, bei 1ms (dunkelblaue Linie bis zur braunen Linie, dann zur pinkfarbenen Linie) sind maximal noch 50V und 7A zulässig, also nur noch 350W. Die letzte Linie stellt den Fall für Gleichstrom (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent), also Dauerbelastung dar, hier sind bei 50V maximal 1,5A zulässig, was einer Dauerverlustleistung von 75W entspricht. MOSFETs, welche nur für Schaltbetrieb und nicht für [[#Linearbetrieb von MOSFETs | Linearbetrieb]] geeignet sind, haben keine Kennlinie für DC. Im normalen Schaltbetrieb liegt der Arbeitspunkt auf der linken Grenzlinie R_DS_ON_MIN. Nur im Linearbetrieb liegt der Arbeitspunkt innerhalb der Fläche, welche durch die Außenlinien begrenzt wird.&lt;br /&gt;
&lt;br /&gt;
[[bild: SOA-BUZ11.png | thumb | 300px| SOA-Diagramm]]&lt;br /&gt;
&lt;br /&gt;
Bei der Anwendung des Diagramms gilt es einiges zu beachten. Die Pulsleistungen sind nur zulässig, wenn der MOSFET vorher kalt ist, sprich ca. 25°C Sperrschichttemperatur hat. War er vorher schon heiß, reduziert sich die zulässige Belastung deutlich. Ebenso dürfen die Pulse nicht zu schnell wiederholt werden, denn dann ist der MOSFET noch vom vorherigen Puls aufgeheizt. Im Fall von DC sind 75W Verlustleistung auch eher ein theoretischer Wert, welcher real nur schwer erreicht werden kann, wenn der MOSFET auf einem sehr großen [[Kühlkörper]] optimal montiert ist. Praktisch liegen die erreichbaren Werte eher bei der Hälfte.&lt;br /&gt;
&lt;br /&gt;
(Anm. Eigentlich müsste für die R_DS_ON Grenzlinie R = U / I der minimale R_DS_ON rauskommen, hier ~40mOhm, es kommen aber ~80mOhm raus. Die Ursache dafür ist unklar, möglicherweise liegt hier ein Sicherheitsfaktor zu grunde).&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Linearbetrieb von MOSFETs ==&lt;br /&gt;
&lt;br /&gt;
Der Großteil der Anwendungen nutzt MOSFETs als Schalter, d.h. der MOSFET ist entweder voll gesperrt oder voll durchgesteuert. Dafür gelten auch all die Hinweise in diesem Artikel. In bestimmten Anwendungen werden MOSFETs aber auch im Linearbetrieb eingesetzt, z.B in linearen Endstufen für Audio, Video, elektronischen Lasten und Stromquellen. Hier muss man einiges beachten. Ein verbreiteter Irrtum besteht darin zu glauben, MOSFETs könne man im Linearbetrieb einfach parallel schalten, weil der positive Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; eine Symmetrierung bewirkt, ähnlich den Emitterwiderständen bei parallelgeschalteten Bipolartransistoren. Das ist &#039;&#039;ausschließlich&#039;&#039; im Schaltbetrieb möglich, und daher falsch! Im Linearbetrieb spielt der Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; keine Rolle, weil der MOSFET selten bis nie komplett durchgesteuert ist. Eben darum ist beim Linearbetrieb der minimale &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; in den meisten Fällen unwichtig und man kann auch eher hochohmige, ältere MOSFETs verwenden, wie z.B. den BUZ11.&lt;br /&gt;
&lt;br /&gt;
Hier wirkt vielmehr der negative Temperaturkoeffizient (TK) der Thresholdspannung &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, vergleichbar dem negativen TK der Basis-Emitter-Spannung von Bipolartransistoren. D.h. mit steigender Temperatur und konstanter Gate-Source-Spannung steigt der Stromfluss der Drain-Source Strecke. In einer Parallelschaltung von MOSFETs würde dies bedeuten, dass der MOSFET mit dem geringfügig größeren Drainstrom (Fertigungstoleranzen) wärmer wird, was zu einem weiter steigenden Drainstrom und damit noch mehr Wärme führt. Damit ist die Schaltung thermisch instabil und würde zum Durchbrennen der MOSFETs führen, einer nach dem Anderen. &lt;br /&gt;
&lt;br /&gt;
Um das zu verhindern muss man relativ große Ausgleichswiderstände in die Source-Leitung der einzelnen MOSFETs schalten, um diese Drift zu kompensieren. Dadurch verschlechtert sich natürlich der Wirkungsgrad des Verstärkers. MOSFETs haben einen TK von typisch -5mV/K für &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, das ist mehr als das doppelte von Bipolartransistoren mit typisch -2mV/K, weshalb die Symmetrierungswiderstände mehr als doppelt so groß sein müssen. Weiterhin muss man beachten, dass die Toleranzen von &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt; sehr groß sind, im Bereich von Volt! Das kann man sinnvoll nicht mehr mit Gatewiderständen symmetrieren, hier muss man die MOSFETs ausmessen und Gruppen mit geringen Toleranzen in einer Schaltung verwenden (engl. matching). &lt;br /&gt;
&lt;br /&gt;
Eine andere Möglichkeit ist die getrennte Ansteuerung der einzelnen MOSFETs, das wird oft in elektronischen Lasten bzw. [[Konstantstromquelle#Konstantstromquelle mit Operationsverstärker und Transistor | Konstantstromquellen]] gemacht. Hier treten keine zusätzlichen Verluste auf und der Mehraufwand in der Ansteuerung ist meist unkritisch.&lt;br /&gt;
&lt;br /&gt;
Weiterhin muss man beachten, dass viele der heutigen HochleistungsMOSFETs intern eine Parallelschaltung vieler kleiner MOSFET-Zellen (z.B. sogenannte Trench-FET) sind, und somit oft für den Linearbetrieb ungeeignet sind. Denn auch dort können einzelne Zellen überhitzen und durchbrennen (Hot Spot). Ob ein MOSFET für den Linearbetrieb tauglich ist steht manchmal im Datenblatt, oft aber eher nicht, eben weil die meisten MOSFETs als Schalter entwickelt und gebaut sind. Typische Vertreter für Linearbetrieb findet man in der [[MOSFET-Übersicht]]. Ein wichtiges Indiz für Linearbetrieb ist eine Kurve für DC im [[#SOA_Diagramm | SOA-Diagramm]]. Meist geht es dort nur bis 10ms, DC fehlt, eben weil DC (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent = Gleichstrom = Linearbetrieb) nicht zulässig ist. Manchmal hat der Hersteller auch &amp;quot;vergessen&amp;quot;, die Kennlinie für DC mit reinzuschreiben, wie z.B. bei [http://www.irf.com/product-info/hi-rel/alerts/fv5-p-09-01-A.pdf IRF], wie in diesem [http://www.mikrocontroller.net/topic/291760#3106758 Beitrag] zu erfahren ist.&lt;br /&gt;
Ein recht gutes Indiz dafür, ob ein FET für den Linearbetrieb taugt, ist die Vorwärtssteilheit. Diese kennzeichnet die Abhängigkeit des Drainstromes von der Ansteuerung am Gate als &amp;lt;math&amp;gt;S = \Delta i_d/\Delta u_{gs}&amp;lt;/math&amp;gt;. Moderne Trench-FET erreichen heute Steilheiten im dreistelligen Bereich und sind für Linearanwendungen völlig unbrauchbar. Zum Vergleich: Der BUZ11 kommt mit gerade einmal 4 bis 5 Siemens daher.&lt;br /&gt;
&lt;br /&gt;
In diesem Beitrag wird die DC-Linie im SOA-Diagramm noch genauer erklärt: [http://www.mikrocontroller.net/topic/319961#3473567 Re: MOSFET Linearbetrieb möglich?]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
* [[Mosfet-Übersicht]]&lt;br /&gt;
* [[IGBT]]&lt;br /&gt;
* [[TRIAC]]&lt;br /&gt;
* [[Kühlkörper]] &lt;br /&gt;
* [[Zwischenkreiskapazität]]&lt;br /&gt;
* [[Treiber]]&lt;br /&gt;
* [[Snippets#Wie_schlie.C3.9Fe_ich_einen_MOSFET_an_einen_Mikrocontroller_an.3F|Wie schließe ich einen Mosfet an einen Mikrocontroller an?]]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/H-Br%C3%BCcken_%C3%9Cbersicht Übersicht H-Brücken]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/168218#1609684 Forumsbeitrag]: MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/186785#new Forumsbeitrag]: nochmal MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/319961#3473567 Forumsbeitrag]: sehr ausführlicher Forumsbeitrag über MOSFETs im Linearbetrieb. Berücksichtigt auch den Spirito-Effekt.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/143324#new Forumsbeitrag]: Über eine elektronische Last, sehr lang&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag]: Logic Level MOSFETs direkt mit einem [[AVR]] treiben.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787855 Forumsbeitrag]: MOSFETs im Linearbetrieb, Laborerfahrungen&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787945 Forumsbeitrag]: MOSFETs für Linearbetrieb&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/269642?goto=2820617#2820617 Forumsbeitrag]: Verpol- und Überspannungsschutz mit MOSFETs&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/425414#4981611 Forumsbeitrag]: Linearbetrieb von MOSFETs&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.elektronikinfo.de/strom/feldeffekttransistoren.htm Feldeffekttransistoren bei elektronikinfo.de]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0207011.htm FET im ELKO]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0510161.htm MOSFET im ELKO]&lt;br /&gt;
* [http://www.sprut.de/electronic/switch/nkanal/nkanal.html MOSFET bei sprut.de]&lt;br /&gt;
* [http://sound.westhost.com/articles/hexfet.htm#51 MOSFETs in Audioendstufen, engl.]&lt;br /&gt;
* [http://irf.custhelp.com/cgi-bin/irf.cfg/php/enduser/std_adp.php?p_faqid=214&amp;amp;p_created=1019728945&amp;amp;p_sid=pt9ITiCj&amp;amp;p_accessibility=0&amp;amp;p_redirect=&amp;amp;p_lva=&amp;amp;p_sp=cF9zcmNoPTEmcF9zb3J0X2J5PSZwX2dyaWRzb3J0PSZwX3Jvd19jbnQ9MTQsMTQmcF9wcm9kcz0mcF9jYXRzPSZwX3B2PSZwX2N2PSZwX3BhZ2U9MSZwX3NlYXJjaF90ZXh0PWxpbmVhcg**&amp;amp;p_li=&amp;amp;p_topview=1 FAQ Answer ID 214 bei IRF zum Linearbetrieb]&lt;br /&gt;
* [http://www.nxp.com/documents/application_note/AN11158.pdf AN11158 - Understanding power MOSFET data sheet parameters] von NXP (PDF)&lt;br /&gt;
* [https://assets.nexperia.com/documents/technical-note/TN00008.pdf TN00008 - Power MOSFET frequently asked questions and answers] von nexperia (PDF)&lt;br /&gt;
* [http://www.infineon.com/dgdl/Infineon+-+Application+Note+-+PowerMOSFETs+-+OptiMOS%E2%84%A2+-+Linear+Mode+Operation+and+SOA+Power+MOSFETs.pdf?fileId=db3a30433e30e4bf013e3646e9381200 AN: Linear Mode Operation andSafe Operating Diagram of Power-MOSFETs] von Infineon (PDF)&lt;br /&gt;
* [http://www.ixys.com/Documents/Articles/Article_Linear_Power_MOSFETs.pdf MOSFETs Withstand Stress of Linear-Mode Operation] Neuentwickelte MOSFETs für Linearbetrieb (PDF)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]] [[Kategorie:Leistungselektronik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107025</id>
		<title>FET</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107025"/>
		<updated>2024-07-19T16:31:47Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* FET-Typen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel versteht sich als Unterpunkt zum Artikel [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein FET (engl. &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor) ist ein  Feldeffekttransistor. Der FET ist ein Bauelement, das im Gegensatz zum Bipolartransistor (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT) mit Spannung und nicht mit Strom gesteuert wird. Unterschieden werden&lt;br /&gt;
* MOSFET = engl. &#039;&#039;&#039;M&#039;&#039;&#039;etall &#039;&#039;&#039;O&#039;&#039;&#039;xide &#039;&#039;&#039;S&#039;&#039;&#039;emiconductor &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor; Metalloxidschicht-FET, größte Teilgruppe der FETs mit isoliertem Gate &lt;br /&gt;
* JFET = engl. &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, Übergangszonen FET, der steuerbare Kanal wird durch einen PN-Übergang wie in einer Diode gebildet&lt;br /&gt;
&lt;br /&gt;
Die drei Anschlüsse eines FETs werden &#039;&#039;Gate&#039;&#039;, &#039;&#039;Drain&#039;&#039; und &#039;&#039;Source&#039;&#039; genannt. Unter Umständen ist ein vierter Anschluß vorhanden, der &#039;&#039;Bulk&#039;&#039; genannt wird. Normalerweise ist Bulk intern mit Source verbunden. Wenn dies nicht der Fall ist, muss diese Verbindung durch den Designer in der Schaltung hergestellt werden.&lt;br /&gt;
&lt;br /&gt;
== FET-Typen ==&lt;br /&gt;
&lt;br /&gt;
FETs werden hauptsächlich unterschieden in N-Kanal und P-Kanal, sowie &amp;quot;selbst sperrend = Anreicherungstyp&amp;quot; (engl. enhancement type) und &amp;quot;selbst leitend = Verarmungstyp&amp;quot; (engl. depletion type). Beim selbstleitenden FET ist der Transistor bei 0V Gate-Source Spannung maximal leitend (durchgesteuert) und wird durch Anlegen einer Spannung ans Gate gesperrt. Beim selbstsperrenden FET (größte Gruppe) ist der Transistor bei 0V Gate-Source Spannung gesperrt und wird durch Anlegen einer Spannung ans Gate leitend. Ist die Linie zwischen Drain und Source durchgezogen handelt es sich um einen selbstleitenden, bei einer gestrichelten Linie um einen selbstsperrenden FET. JFETs gibt es nur als Verarmungstyp.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Artikel wird nur der &#039;&#039;&#039;selbstsperrende&#039;&#039;&#039; MOSFET betrachtet, weil nur dieser einen althergebrachten Bipolartransistor „ersetzen“ kann.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Typen von Feldeffekttransistoren&amp;lt;br/&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Typ &lt;br /&gt;
! N-Kanal &lt;br /&gt;
! P-Kanal&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| JFET&amp;lt;br/&amp;gt;SFET&amp;lt;br/&amp;gt;(selbst leitend) || [[bild:JFET-N.png|center]]&lt;br /&gt;
* drittgrößte Gruppe&lt;br /&gt;
* bislang nur für kleine Leistungen verfügbar&lt;br /&gt;
* JFETs mit hoher Leistung sind im Kommen&lt;br /&gt;
* Eingangsstufen von OPVs&lt;br /&gt;
* Eingangsstufen von HF-Verstärkern bis in den GHz-Bereich&lt;br /&gt;
** Typen mit zwei Gates zum Regeln und Mischen üblich&lt;br /&gt;
* als einfache [[Konstantstromquelle]] geeignet&lt;br /&gt;
| [[bild:JFET-P.png|center]]&lt;br /&gt;
* selten&lt;br /&gt;
* ersatzhalber für „fehlenden“ P-Kanal-Verarmungs-MOSFET&lt;br /&gt;
* Ältester Vertreter der Unipolartransistoren&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Anreicherungstyp&amp;lt;br/&amp;gt;(selbst sperrend)&amp;lt;br/&amp;gt;&#039;&#039;Enhancement Mode&#039;&#039; || [[bild:MOS-EN.png|center]]&lt;br /&gt;
* Mit Abstand größte Gruppe; wenn nicht besonders erwähnt ist &#039;&#039;dieser&#039;&#039; gemeint&lt;br /&gt;
* sehr viele Typen erhältlich&lt;br /&gt;
* Auch als Doppel-MOSFET oder zusammen mit P-Kanal-MOSFET in einem Gehäuse gängig&lt;br /&gt;
| [[bild:MOS-EP.png|center]]&lt;br /&gt;
* zweitgrößte Gruppe&lt;br /&gt;
* bei gleicher Geometrie etwas schlechter als ein N-Kanal Typ&lt;br /&gt;
* Auch als Doppel-MOSFET oder zusammen mit N-Kanal-MOSFET in einem Gehäuse gängig&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Verarmungstyp&amp;lt;br/&amp;gt;(selbst leitend)&amp;lt;br/&amp;gt;&#039;&#039;Depletion Mode&#039;&#039; || [[bild:MOS-DN.png|center]]&lt;br /&gt;
* selten aber käuflich&lt;br /&gt;
* Sinnvolle Anwendungen ergeben sich vor allem in Reihenschaltungen&lt;br /&gt;
| [[bild:MOS-DP.png|center]]&lt;br /&gt;
* Nicht in freier Wildbahn gesichtet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Vorteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Niedrigere Verluste als bei Bipolartransistoren.&lt;br /&gt;
* Sehr schnelles Schalten möglich, daher für sehr hohe Frequenzen geeignet (keine Speicherzeit wie beim BJT).&lt;br /&gt;
* Einfaches Parallelschalten im Schaltbetrieb, da Unterschiede im &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; durch den positiven Temperaturkoeffizienten ausgeglichen werden.&lt;br /&gt;
* Leistungslose Ansteuerung im statischen Fall, jedoch hohe Umladeverluste am Gate!&lt;br /&gt;
* oft preiswerter als vergleichbare Bipolartransistoren (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT)&lt;br /&gt;
* Relativ unempfindlich gegen Überspannung zwischen Drain und Source. Bei Überschreitung der Maximalspannung zwischen Drain und Source findet ein sogenannter &amp;quot;Durchbruch&amp;quot; statt. Dies ist vergleichbar mit dem Zener-Effekt. Ist die Energiemenge begrenzt, ist dieser Durchbruch reversibel und der FET wird NICHT zerstört. Beim Bipolartransistor ist die dafür zulässige Energiemenge viel kleiner, weil die den Strom durchquerende Fläche zu Hotspots neigt.&lt;br /&gt;
&lt;br /&gt;
=== Nachteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Nur bedingt für hohe Spannungen [[Transistor#Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein ? |geeignet]], die ON-Verluste sind ab ca. 250V höher als bei einem [[IGBT]]. &lt;br /&gt;
* Parasitäre Diode parallel zur Drain-Source Strecke ist immer enthalten, das (Ab-)Schaltverhalten dieser Dioden ist meist schlechter als separate Dioden, was häufig zu unerwünschten Schwingungen führt.&lt;br /&gt;
* Empfindlicher gegen ESD am Gate als BJT&lt;br /&gt;
* Positiver Temperaturkoeffizient (TK), der &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; ist stark temperaturabhängig und steigt von 25°C (Datenblattangabe) auf 150°C ungefähr um den Faktor 2. Dadurch steigen auch die Verluste und damit die Erwärmung des Bauteiles.&lt;br /&gt;
&lt;br /&gt;
=== Erklärung der wichtigsten Datenblattwerte ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:7em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | Beispiel&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Drain Source Voltage &amp;lt;br&amp;gt;(Breakdown) || V(BR)_DSS &amp;lt;br&amp;gt; V_DS || 75 V || Maximale Spannungsfestigkeit des Bauteiles zwischen Drain und Source&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Drain Current  || I_D(on)   || 55 A @ 125 °C  || Maximaler Dauerstrom bei 125°C Gehäusetemperatur &lt;br /&gt;
|-&lt;br /&gt;
| Pulsed Drain Current || ID_pulse &amp;lt;br&amp;gt; I_CD(on) || 240 A || Maximaler Pulsstrom (Achtung die zulässige Zeitdauer des Pulses kann nur über die maximale Junctiontemperatur ermittelt werden)&lt;br /&gt;
|-&lt;br /&gt;
| Gate Charge || Q&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; ||  320 nC || Gate-Ladung in Coloumb oder Amperesekunden, die zum Ein- oder Ausschalten des MOSFET erforderlich ist. Dies berücksichtigt die Nichtlinearität der Kapazität und (richtig?) den Miller-Effekt. Mit dieser Angabe wird der für das Schalten von Leistungs-MOSFET benötigte Gatestrom für eine bestimmte Schaltzeit ermittelt. Dieser liegt üblicherweise im einstelligen Ampere-Bereich. Moderne Typen mit extra geringer Gate-Ladung können so schneller schalten.&lt;br /&gt;
|-&lt;br /&gt;
| Repetetive Avalanche Energy || t_sc ||  280 mJ || Maximale Energie, welche beim Avalanche Durchbruch bei Überschreiten der maximalen Drain-Source Spannung im MOSFET bei z.&amp;amp;nbsp;B. 1% Puls/Pausen Verhältnis regelmäßig auftreten darf, ohne den FET zu schädigen&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_ON&amp;lt;/sub&amp;gt; ||  0,01 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;25°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_on&amp;lt;/sub&amp;gt; ||  0,021 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;175°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Thermal Resistance&amp;lt;br&amp;gt;(junction-case) ||  R&amp;lt;sub&amp;gt;th_JC&amp;lt;/sub&amp;gt; ||  0,8 K/W || Thermischer Widerstand im Transistor vom der „Sperrschicht“ (junction eines Bipolartransistors, ja altmodischer Index, hier besser „Kanal“) bis zur Rückseite oder bestmöglichen Kühlmöglichkeit des Transistorgehäuses (case)&lt;br /&gt;
|-&lt;br /&gt;
| Gate-Source&amp;lt;br&amp;gt;Threshold Voltage ||  V_GS(th) || 2,0-4,5 V || Gatespannung, ab welcher der Transistor minimal leitend wird (I_D typisch 100-200µA), große Toleranz, typisch 1:2 zwischen Minimum und Maximum&lt;br /&gt;
|-&lt;br /&gt;
| Turn-on Delay ||  t_d(on) || 40 ns ||  Verzögerung zwischen dem Einschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Rise Time ||  t_r || 200 ns ||  Anstiegszeit des Transistorstromes am Drain&lt;br /&gt;
|-&lt;br /&gt;
| Turn-off Delay || t_d(off) || 120 ns ||  Verzögerung zwischen Abschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Fall Time ||  t_f  || 60 ns || Abfallzeit des Transistorstromes am Drain  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die oben genannten Zeiten gelten ausschließlich unter den angegebenen Messbedingungen (Gatewiderstand, Treiberspannung, sowie einer &#039;&#039;&#039;FET-Teperatur von 25°C!&#039;&#039;&#039;) und müssen für die eigene Anwendung ggf. neu berechnet werden. Meist wird man sie eher messen, weil die Rechung zu aufwändig und bisweilen unmöglich ist. &lt;br /&gt;
&lt;br /&gt;
==== Gate-Source Threshold Voltage ====&lt;br /&gt;
Gerade bei der &#039;&#039;&#039;Gate-Source Threshold Voltage &amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt;&#039;&#039;&#039; gibt es hier immer wieder Verwirrung. Sie gibt an, ab welcher Spannung der MOSFET anfängt, minimal leitfähig zu werden. Diese Spannung ist technologisch bedingt auch heute noch einer starken Toleranz unterworfen, typischerweise hat der Bereich  bei dem der FET zu leiten beginnt eine Spreizung von etwa 1:2 zwischen Minimum und Maximum. Welche Spannung man nun wirklich anlegen muss, um den gewünschten &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt; zu erreichen kann der Tabelle im jeweiligen Datenblatt entnommen werden. Dabei unbedingt die angegebene Gatespannung beachten, nur dieser Wert ist garantiert!.&lt;br /&gt;
&lt;br /&gt;
[[bild: IRLZ34N_R_DS_ON.png | thumb | 800px | R_DS_ON im Datenblatt des IRLZ34N]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Die Kurvenschar von &amp;lt;math&amp;gt;I_D&amp;lt;/math&amp;gt; über &amp;lt;math&amp;gt;U_{DS}&amp;lt;/math&amp;gt; in Abhängigkeit von &amp;lt;math&amp;gt;U_{GS}&amp;lt;/math&amp;gt; stellt immer nur typische Werte dar, keine garantierten Extremwerte (engl. worst case). Als Standardwerte kann man 10-15V für einen Standardtypen und ca. 3-5V für einen Logic Level MOSFET (LL-FET) ansetzen. Kleinsignal-FETs leiten schon ab ca 1V. Bei Ansteuerung mit 5V benötigt man also einen Typen, der &#039;&#039;&#039;sicher&#039;&#039;&#039; bei 5V voll durchgesteuert ist, z.B. IRLZ34N. Bei 3,3V ist er bereits nicht mehr zuverlässig nutzbar. Es gibt auch Typen mit noch geringerer Spannung für Vollaussteuerung. Wer einen BUZ11 (&amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt; 4V max.) mit 5V ansteuert riskiert ein Abfackeln des MOSFETs, denn je nach Toleranz kann er bereit ganz gut aufgesteuert sein oder auch nicht. Das soll in den nachfolgenden Diagrammen dargestellt werden. Zunächst die Transferkennlinie. Sie gibt an, wieviel Drainstrom in Abhängigkeit der Gatespannung fließen kann, wobei die Drainspannung konstant ist, hier im Beispiel 25V. Ein typischer BUZ11 (mittlere, schwarze Kurve) fängt bei 3V zu leiten an und erreicht bei 6V am Gate ca. 17A. Erwischt man nun ein kritisches Exemplar, das erst bei 4V zu leiten anfängt (rechte, rote Kurve um 1V parallel verschoben), so kann dieser bei 6V nur 8A leiten, für 17A braucht er 7V. Der günstige Fall, daß der BUZ11 schon bei 2V anfängt zu leiten sieht man in der linken, roten Kurve.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UGS_ID.png | thumb | 800px | Transferkennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Wenn man die drei Fälle (min, typ, max) mit 5V ansteuert, erhält man die Arbeitspunkte 1-3 mit einem maximal schaltbaren Drainstrom von 2,5, 8 und 17A. Diese kann man in das Ausgangskennlinienfeld übertragen. Wir nehmen max. 1V U_DS an. Es ergeben sie die gleichen Ströme. Der maximale Drainstrom weicht um -5,5/+9A vom typischen Fall ab. Erhöht man nun die Gate-Source Spannung auf 10V (Arbeitspunkt 4), schwankt der maximal schaltbare Drainstrom nur noch um -2/+2A, außerdem liegt er mit 30A deutlich höher. Der BUZ11 ist somit sicher durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UDS_ID_UGS.png | thumb | 800px | Ausgangskennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
==== Parasitäre Diode ====&lt;br /&gt;
&lt;br /&gt;
Der Schwerpunkt in der FET-Entwicklung liegt auf den geringst-möglichen &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt;,  die Diode entsteht auf Grund des Herstellungsprozesses, und wird nur nachrangig verbessert, da viele Optimierungsversuche auch einen Einfluss auf wichtige Kennwerte des FETs hatten und haben.&lt;br /&gt;
Daher muss sorgfältig geprüft werden, ob die Schaltgeschwindigkeit, die Recovery-Time und die damit verbundenen Verluste sowie die dabei erzeugte unerwünschte EMV-Abstahlung tolerierbar ist, oder nicht.&lt;br /&gt;
Hier hilft es oft eine optimierte Diode / Schottky-Diode zum FET parallel zu schalten. Ganz ausblenden läßt sich die parasitäre Diode jedoch nicht, jedoch kann man den Anteil des Stromes beeinflussen, der über die intere Diode fliest.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039; Parasitäre Diode des FETs  &#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Current  ||  I_S || 75A || Maximaler Dauerstrom der parasitären Diode, meist identisch zum maximalen Dauerstrom des MOSFETs&lt;br /&gt;
|-&lt;br /&gt;
| Forward Voltage ||  V_SD || 1,3V || Spannungsfall an der parasitären Diode &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Time ||  t_rr || 120ns || Zeit, welche die Elektronen brauchen um aus der leitenden Diode vollständig abzufließen. Während dieser Zeit fließt der Strom in &#039;&#039;&#039;Rückwärtsrichtung&#039;&#039;&#039; durch die Diode und erzeugt relativ viel Verlustleistung. &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Charge ||  Q_rr || 60nC || Ladungsmenge, die während t_rr rückwärts durch die Diode fließt.  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Haupttypen und Gatespannungslevel ==&lt;br /&gt;
&lt;br /&gt;
===Unterschied N-Kanal / P-Kanal FET===&lt;br /&gt;
&lt;br /&gt;
Im Schaltsymbol werden die MOSFET-Typen meist durch die Pfeilrichtung in der Mitte des Symbols (eigentlich &amp;quot;Bulk&amp;quot;) vom oder zum Gate unterschieden.  Zeigt der Pfeil zum Gate hin, handelt es sich um einen N-Kanal-FET, zeigt der Pfeil vom Gate weg um einen P-Kanal FET.&lt;br /&gt;
&lt;br /&gt;
Der große Vorteil des N-Kanal FETs (Elektronenleitung) ist, daß er immer niederohmiger ist, als ein gleich großer P-Kanal FET (Löcherleitung). Daher sind P-Kanal Typen bei vergleichbaren Werten auch immer größer = teuerer da weniger Chips auf einem Wafer Platz haben.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET muss die Gatespannung positiv gegenüber Source sein. Dabei wird der FET dann leitend, wenn die sogenannte &amp;quot;threshold voltage&amp;quot; (Schwellenspannung) erreicht wird. Eine typische Anwendung ist z.&amp;amp;nbsp;B. ein &#039;&#039;&#039;Low-Side Schalter&#039;&#039;&#039;: Source an GND, Drain an die Last, Ansteuerung des N-Kanal FETs mit 12V gleichbedeutend mit 12V ÜBER den Source = GND Potential.&lt;br /&gt;
 &lt;br /&gt;
Beim P-Kanal FET als HS-Schalter muss die Gatespannung negativer=niedriger als das Sourcepotential sein.Beispiel.&lt;br /&gt;
Beispiel:  &lt;br /&gt;
Lastspannung = 400V d.h. Source an 400V, Last zwischen Drain und GND, Ansteuerung des P-Kanal FETs mit 388V, also 12V UNTER dem Sourcepotential.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET als HS-Schalter muss die Gatespannung positver=höher als das Sourcepotential sein.&lt;br /&gt;
Beispiel:&lt;br /&gt;
Lastspannung = 400V d.h. Drain an 400V, die Last zwischen Source und GND, Ansteuerung des N-Kanal FETs mit 412V, also 12V ÜBER dem Sourcepotential.&lt;br /&gt;
In diesem Fall ist aber eine zusätzliche Spannungsquelle erforderlich, denn der FET wird mit einer Spannung über der Lastspannung eingeschaltet. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weblinks&#039;&#039;&#039;&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-1-of-2/?utm_source=eetimes&amp;amp;utm_medium=networksearch A primer on high-side FET load switches (Part 1 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products, Micrel, Inc., 5/3/2007 4:14 PM EDT, www.eetimes.com&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-2-of-2/ A primer on high-side FET load switches (Part 2 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products. Micrel, Inc., 5/7/2007 1:36 PM EDT, www.eetimes.com&lt;br /&gt;
* [http://www.vishay.com/docs/70611/70611.pdf AN804 P-Channel MOSFETs, the Best Choice for High-Side Switching (PDF)] von Vishay Siliconix&lt;br /&gt;
&lt;br /&gt;
===Unterschied Logic-Level / &amp;quot;Normal&amp;quot;-Level===&lt;br /&gt;
&lt;br /&gt;
Den meisten FETs ist gemein, daß sie mit einer Spannung von 10..15V angesteuert werden müssen, um den minimalen Einschaltwiderstand zu erreichen. Diese FETs lassen sich nicht ohne weiteres mit einem CMOS-Pegel von 5V ansteuern. Es gibt jedoch für diesen Anwendungsfall sogenannte &amp;quot;Logic Level&amp;quot; (LL) FETs, die schon bei einer Gatespannung von etwa 4,5V voll durchgesteuert sind. Einige Kleinsignal-FETs sind schon ab ca. 1,2V voll durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
== Beispiel zur Bauteiledimensionierung ==&lt;br /&gt;
&lt;br /&gt;
=== Spannungsfestigkeit ===&lt;br /&gt;
&lt;br /&gt;
Die höchste vorkommende Betriebsspannung + Abschaltüberspannung soll kleiner als ca. 80% der Spannungsfestigkeit des Bauteiles sein. &lt;br /&gt;
&lt;br /&gt;
Achtung: Zwischen dem je nach Anwendungsfall erforderlichen Pufferkondensator und dem FET wird es immer eine parasitäre Induktivität geben.&lt;br /&gt;
Abhängig von Schaltgeschwindigkeit und Induktivität wird im Schaltmoment eine mehr oder weniger große Übrspannungsspitze produziert. Dieser Peak&lt;br /&gt;
addiert sich auf die aktuelle Versorgungsspannung.&lt;br /&gt;
&lt;br /&gt;
Überschlagsrechnung als Beispiel:&lt;br /&gt;
* Schaltgeschwindigkeit:  dI/dt = -100A/µs (= Abschalten von 5A innerhalb 50ns),&lt;br /&gt;
* Induktivität:   L = 1µH (~ 1 m loses, ungebündeltes Kabel)&lt;br /&gt;
* dU=-L*dI/dt = -1µH * (-100A / 1µs) = 100V&lt;br /&gt;
&lt;br /&gt;
Dies bedeutet, daß an der &amp;quot;Induktivität&amp;quot; zwischen Transistor und Kondensator - Aufgrund von Selbstinduktion im Schaltmoment - ein Überspannungspuls von ca. 100V entsteht, der auf die Betriebsspannung aufzuschlagen ist, also lieber die Leitung kürzer machen, und - sofern möglich - nicht ganz so schnell schalten.&lt;br /&gt;
&lt;br /&gt;
=== Stromtragfähigkeit ===&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt ist eine Stromtragfähigkeit bei 25°C, und meist noch bei einer höheren Temperatur z.B. 125°C, 150°C oder 175°C Kühlfahnentemperatur angegeben. Dieser Wert ist als ERSTE Entscheidungsgrundlage ausreichend, aber aus der theoretisch abführbaren Verlustleistung errechnet, und&lt;br /&gt;
* dient zum qualitativen Vergleich von Transistoren bezüglich ihres R_ds(on) und ihres Wärmewiderstands.&lt;br /&gt;
* ist für die Dimensionierung einer Schaltung nur als Richtwert zu interpretieren. &lt;br /&gt;
* ist ohne Schaltverluste genannt, und daher nur für einen Schaltbetrieb von wenigen Hz gültig. Außerdem wird ein annähernd idealer Kühlkörper unterstellt, der trotz der Verlustleistung das Gehäuse des Transistors auf der angegebenen Temperatur halten kann.&lt;br /&gt;
* entbindet einen nicht davon den Kopf einzuschalten... siehe die nachfolgenden Zeilen.&lt;br /&gt;
* Liegt der Strom für den die Schaltung entwickelt wird mit ca. 10..20% Abstand unter dem Datenblattwert von 125°C ist dieses Bauteil vermutlich verwendbar (siehe Detailberechnungen unten !).   &lt;br /&gt;
* Ist der benötigte Strom im Bereich oder größer als der zulässige bei 125°C sollte entweder ein anderer Typ eingesetzt oder mehrere FETs &#039;&#039;des gleichen Typs&#039;&#039; parallelgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
== Verlustleistung ==&lt;br /&gt;
&lt;br /&gt;
Hier wird eine Näherung für eine getaktete Anwendung betrachtet. In einem Transistor treten sowohl beim Ein- und Ausschalten, als auch während der Einschaltphase Verluste im Bauteil auf. Diese Verluste führen zu einer Bauteilerwärmung. Die dabei entstehende Temperatur darf die maximal zulässige Bauteiletemperatur nie überschreiten. Bei den ersten Projekten ist zu empfehlen eine berechnete Chiptemperatur von ca. 125°C nicht zu überschreiten. Fast alle aktuell verfügbaren FETs nennen im Datenblatt eine Temperatur von 175°C als ihre maximale Chiptemperatur.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:32em&amp;quot; &lt;br /&gt;
|+ &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Symbol&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Wert&lt;br /&gt;
|-&lt;br /&gt;
| Betriebsspannung || U&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 70 V&lt;br /&gt;
|-&lt;br /&gt;
| Nennstrom || I&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 30 A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source Widerstand bei&amp;lt;br&amp;gt;Chiptemperatur: 125°C&amp;lt;br&amp;gt; Gatespannung: 10V || R&amp;lt;sub&amp;gt;DS&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt;&amp;lt;/sub&amp;gt; || 17 mΩ&lt;br /&gt;
|-&lt;br /&gt;
| Pulsbreite || t&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt; || 150 µs&lt;br /&gt;
|-&lt;br /&gt;
| Schaltfrequenz || ƒ&amp;lt;sub&amp;gt;schalt&amp;lt;/sub&amp;gt; || 5 kHz&amp;lt;br&amp;gt;T = 200µs&lt;br /&gt;
|-&lt;br /&gt;
| Einschaltzeit (risetime) || t&amp;lt;sub&amp;gt;r&amp;lt;/sub&amp;gt; || 500 ns&lt;br /&gt;
|-&lt;br /&gt;
| Ausschaltzeit (falltime) || t&amp;lt;sub&amp;gt;ƒ&amp;lt;/sub&amp;gt; || 800 ns &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Leitend-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Während der FET bei [[PWM]]-Ansteuerung eingeschaltet ist, erzeugt er Verlustleistung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
P_\text{ON}&lt;br /&gt;
 = {I_\mathrm{N}}^2 \cdot R_\mathrm{DS_\mathrm{ON}} \cdot \frac{t_\mathrm{ON}}{T}&lt;br /&gt;
 = 30^2A^2 \cdot 17m\Omega  \cdot \frac{150\mu s}{200\mu s} = 11{,}5W&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schalt-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Vereinfachter Ansatz.&lt;br /&gt;
&lt;br /&gt;
Einschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_r}&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_r}{T} \\&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot 70V \cdot 30A \cdot \frac{500ns}{200\mu s}=1{,}3W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_f} &lt;br /&gt;
&amp;amp;=\tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_f}{T}\\&lt;br /&gt;
&amp;amp;=\tfrac14 \cdot 70V \cdot 30A \cdot \frac{800ns}{200\mu s}=2{,}1W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ und genauer kann man rechnen, wenn die Ein- Ausschaltenergie im Datenblatt angegeben ist. Aber Achtung! Die  Randbedingungen unter denen die genannte Energie ermittelt wurde, müssen genau so zutreffen.&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_f} = f_{schalt} \cdot E_{ON}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_r} = f_{schalt} \cdot E_{OFF}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Gesamtverlustleistung beträgt also in etwa 15W.&lt;br /&gt;
&lt;br /&gt;
Damit muß ein entsprechender [[Kühlkörper]] ausgelegt und die Chiptemperatur berechnet werden. z.&amp;amp;nbsp;B.:&lt;br /&gt;
* Kühlkörper mit einem R_th von 0,2K/W&lt;br /&gt;
* max. Umgebungstemperatur +60°C&lt;br /&gt;
* R_th &amp;quot;junction-case&amp;quot; des FETs 0,8K/W&lt;br /&gt;
* R_th der Wärmeleitfolie zwischen FET und Kühlkörper ca. 2,0K/W&lt;br /&gt;
* R_th gesamt: 3,0K/W &amp;lt;br&amp;gt;&lt;br /&gt;
* Bei einer Verlustleistung von 18W und einer Umgebungstemperatur von 60°C hat der Chip eine Temperatur von ca. 18W * 3,0K/W +60°C = 114°C. ==&amp;gt; o.k.!&lt;br /&gt;
&lt;br /&gt;
Unter Berücksichtigung der Tatsache, daß hier viele Vereinfachungen vorgenommen, und die Art der Last nicht beachtet wurde ist es sinnvoll, einen gewissen Sicherheitsabstand zu den zulässigen Maximalwerten einzuhalten. Daher ist es empfehlenswert, die Chiptemperatur auf ca. 125°C zu beschränken. &lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist hier die parasitäre Diode im FET nicht berücksichtigt.&lt;br /&gt;
Wenn während der &amp;quot;off&amp;quot; Zeit ein Strom über die Diode fließt (Reverse recovery current oder Freilaufstrom), muß die dadurch &#039;&#039;&#039;zusätzlich&#039;&#039;&#039; entstehende Verlustleistung in die obige Berechnung der maximalen Chiptemperatur mit einfließen.&lt;br /&gt;
&lt;br /&gt;
==Treiberleistung==&lt;br /&gt;
&lt;br /&gt;
Auch wenn der MOSFET ein spannungsgesteuertes Bauelement ist, muss trotzdem bei jedem Einschalten und bei jedem Ausschalten die Gatekapazität umgeladen werden. Bei älteren Leistungs-FET - oder bei einem schlechten Design (!) - muss sogar teilweise mit negativer Spannung am Gate gearbeitet werden, um eine vollständige Sperrung zu erreichen.&lt;br /&gt;
Diese Umladung muss möglichst schnell erfolgen, um die Verluste im FET während der Umschaltphase zu minimieren. Dazu findet ein [[Mosfet-Übersicht#MOSFET-Treiber|MOSFET-Treiber]] Verwendung. Hier eine detaillierte Beschreibung zum [[Treiber]].&lt;br /&gt;
&lt;br /&gt;
Da die Gatekapazität nicht direkt im Datenblatt enthalten ist kann man sich mit der Eingangskapazität Ciss behelfen. Im Arbeitspunkt ist die Gatekapazität ungefähr 5x größer als der im Datenblatt für Ciss angegebene Wert. &lt;br /&gt;
Daher berechnet sich die Treiberleistung wie folgt: &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{Treiber} = C \cdot U^2 \cdot f = 5 \cdot C_\text{íss} \cdot U_\text{Gate}^2 \cdot f_\text{schalt}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1.Beispiel, kleine MOSFET-Steuerung mit niedriger Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 4{,}8\,\text{nF} \cdot 15\,\text{V}^2 \cdot 10\,\text{kHz} = 54\,\text{mW}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2.Beispiel, sehr große MOSFET-Steuerung für Induktionsheizung mit sehr hoher Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 24\,\text{nF} \cdot 15\,\text{V}^2 \cdot 250\,\text{kHz} = 6{,}75\,\text{W}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung, so ein MOSFET-Treiber hat auch einen Eigenverbrauch, der leicht zwischen 0,5 und 1 W liegen kann.&lt;br /&gt;
&lt;br /&gt;
Bei niedrigen PWM-Frequenzen kann man Logic Level MOSFETs auch direkt per CMOS-Ausgang ansteuern, z.B. mit einem [[AVR]], wie in diesem [http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag] zu sehen ist.&lt;br /&gt;
&lt;br /&gt;
== Low- und High-Side ==&lt;br /&gt;
&lt;br /&gt;
Definition LS- und HS:&lt;br /&gt;
 &lt;br /&gt;
;Low-Side Schalter: Der FET schaltet eine Last gegen GND - auch als LS-Schalter bezeichnet.&lt;br /&gt;
;High-Side Schalter: Der FET schaltet eine Last an die Versorgungsspannung – auch als HS-Schalter bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Anregungen oder Fragen auch gerne per Email an [http://www.mikrocontroller.net/user/show/powerfreak Powerfreak]. Dieser Artikel kann dadurch regelmäßig erweitert und ggf. durch ein FAQ ergänzt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== SOA Diagramm ==&lt;br /&gt;
&lt;br /&gt;
SOA-Diagramm (engl. &#039;&#039;&#039;S&#039;&#039;&#039;afe &#039;&#039;&#039;O&#039;&#039;&#039;perating &#039;&#039;&#039;A&#039;&#039;&#039;rea, sicherer Arbeitsbereich) beschreibt die zulässige Verlustleistung eines Transistors in Anhängigkeit des Drainstroms (I_D), der Drain-Source Spannung (U_DS) und der Pulsbreite. Als Beispiel sei hier der BUZ 11 genannt. Im nachfolgenden Diagramm ist das SOA-Diagramm dargestellt. Wie ist es zu verstehen? Zunächst gibt es eine Grenze auf der linken Seite, die schräge, dunkelblaue Line. Diese wird durch den minimalen R_DS_ON festgelegt, hier wirkt der MOSFET wie ein ohmscher Widerstand. Mehr Strom kann bei einer bestimmten Spannung nicht fließen. Die zweite Grenzlinie ist ganz rechts die pinkfarbene Linie, sie stellt die maximale Sperrspannung des MOSFET dar. Die dritte Grenze ist der maximal zulässige Drainstrom, hier im Beispiel 120A, dargestellt durch die gelbe Linie. Die maximale Spannung zwischen Drain und Source sowie der Drainstrom sind abhängig von der Pulsbreite, mit welcher der MOSFET betrieben wird. Bei nur 2,5µs Pulsbreite (Rechteckimpuls) müssen die beiden Parameter sich innerhalb der Fläche bewegen, welche durch die dunkelblaue, gelbe und die pinkfarbene Line begrenzt wird. Im Extremfall dürfen 50V anliegen und 120A fließen, das sind satte 6kW Pulsleistung! Werden die Pulse breiter, so sinken die zulässigen Ströme und Spannungen, bei 1ms (dunkelblaue Linie bis zur braunen Linie, dann zur pinkfarbenen Linie) sind maximal noch 50V und 7A zulässig, also nur noch 350W. Die letzte Linie stellt den Fall für Gleichstrom (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent), also Dauerbelastung dar, hier sind bei 50V maximal 1,5A zulässig, was einer Dauerverlustleistung von 75W entspricht. MOSFETs, welche nur für Schaltbetrieb und nicht für [[#Linearbetrieb von MOSFETs | Linearbetrieb]] geeignet sind, haben keine Kennlinie für DC. Im normalen Schaltbetrieb liegt der Arbeitspunkt auf der linken Grenzlinie R_DS_ON_MIN. Nur im Linearbetrieb liegt der Arbeitspunkt innerhalb der Fläche, welche durch die Außenlinien begrenzt wird.&lt;br /&gt;
&lt;br /&gt;
[[bild: SOA-BUZ11.png | thumb | 300px| SOA-Diagramm]]&lt;br /&gt;
&lt;br /&gt;
Bei der Anwendung des Diagramms gilt es einiges zu beachten. Die Pulsleistungen sind nur zulässig, wenn der MOSFET vorher kalt ist, sprich ca. 25°C Sperrschichttemperatur hat. War er vorher schon heiß, reduziert sich die zulässige Belastung deutlich. Ebenso dürfen die Pulse nicht zu schnell wiederholt werden, denn dann ist der MOSFET noch vom vorherigen Puls aufgeheizt. Im Fall von DC sind 75W Verlustleistung auch eher ein theoretischer Wert, welcher real nur schwer erreicht werden kann, wenn der MOSFET auf einem sehr großen [[Kühlkörper]] optimal montiert ist. Praktisch liegen die erreichbaren Werte eher bei der Hälfte.&lt;br /&gt;
&lt;br /&gt;
(Anm. Eigentlich müsste für die R_DS_ON Grenzlinie R = U / I der minimale R_DS_ON rauskommen, hier ~40mOhm, es kommen aber ~80mOhm raus. Die Ursache dafür ist unklar, möglicherweise liegt hier ein Sicherheitsfaktor zu grunde).&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Linearbetrieb von MOSFETs ==&lt;br /&gt;
&lt;br /&gt;
Der Großteil der Anwendungen nutzt MOSFETs als Schalter, d.h. der MOSFET ist entweder voll gesperrt oder voll durchgesteuert. Dafür gelten auch all die Hinweise in diesem Artikel. In bestimmten Anwendungen werden MOSFETs aber auch im Linearbetrieb eingesetzt, z.B in linearen Endstufen für Audio, Video, elektronischen Lasten und Stromquellen. Hier muss man einiges beachten. Ein verbreiteter Irrtum besteht darin zu glauben, MOSFETs könne man im Linearbetrieb einfach parallel schalten, weil der positive Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; eine Symmetrierung bewirkt, ähnlich den Emitterwiderständen bei parallelgeschalteten Bipolartransistoren. Das ist &#039;&#039;ausschließlich&#039;&#039; im Schaltbetrieb möglich, und daher falsch! Im Linearbetrieb spielt der Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; keine Rolle, weil der MOSFET selten bis nie komplett durchgesteuert ist. Eben darum ist beim Linearbetrieb der minimale &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; in den meisten Fällen unwichtig und man kann auch eher hochohmige, ältere MOSFETs verwenden, wie z.B. den BUZ11.&lt;br /&gt;
&lt;br /&gt;
Hier wirkt vielmehr der negative Temperaturkoeffizient (TK) der Thresholdspannung &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, vergleichbar dem negativen TK der Basis-Emitter-Spannung von Bipolartransistoren. D.h. mit steigender Temperatur und konstanter Gate-Source-Spannung steigt der Stromfluss der Drain-Source Strecke. In einer Parallelschaltung von MOSFETs würde dies bedeuten, dass der MOSFET mit dem geringfügig größeren Drainstrom (Fertigungstoleranzen) wärmer wird, was zu einem weiter steigenden Drainstrom und damit noch mehr Wärme führt. Damit ist die Schaltung thermisch instabil und würde zum Durchbrennen der MOSFETs führen, einer nach dem Anderen. &lt;br /&gt;
&lt;br /&gt;
Um das zu verhindern muss man relativ große Ausgleichswiderstände in die Source-Leitung der einzelnen MOSFETs schalten, um diese Drift zu kompensieren. Dadurch verschlechtert sich natürlich der Wirkungsgrad des Verstärkers. MOSFETs haben einen TK von typisch -5mV/K für &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, das ist mehr als das doppelte von Bipolartransistoren mit typisch -2mV/K, weshalb die Symmetrierungswiderstände mehr als doppelt so groß sein müssen. Weiterhin muss man beachten, dass die Toleranzen von &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt; sehr groß sind, im Bereich von Volt! Das kann man sinnvoll nicht mehr mit Gatewiderständen symmetrieren, hier muss man die MOSFETs ausmessen und Gruppen mit geringen Toleranzen in einer Schaltung verwenden (engl. matching). &lt;br /&gt;
&lt;br /&gt;
Eine andere Möglichkeit ist die getrennte Ansteuerung der einzelnen MOSFETs, das wird oft in elektronischen Lasten bzw. [[Konstantstromquelle#Konstantstromquelle mit Operationsverstärker und Transistor | Konstantstromquellen]] gemacht. Hier treten keine zusätzlichen Verluste auf und der Mehraufwand in der Ansteuerung ist meist unkritisch.&lt;br /&gt;
&lt;br /&gt;
Weiterhin muss man beachten, dass viele der heutigen HochleistungsMOSFETs intern eine Parallelschaltung vieler kleiner MOSFET-Zellen (z.B. sogenannte Trench-FET) sind, und somit oft für den Linearbetrieb ungeeignet sind. Denn auch dort können einzelne Zellen überhitzen und durchbrennen (Hot Spot). Ob ein MOSFET für den Linearbetrieb tauglich ist steht manchmal im Datenblatt, oft aber eher nicht, eben weil die meisten MOSFETs als Schalter entwickelt und gebaut sind. Typische Vertreter für Linearbetrieb findet man in der [[MOSFET-Übersicht]]. Ein wichtiges Indiz für Linearbetrieb ist eine Kurve für DC im [[#SOA_Diagramm | SOA-Diagramm]]. Meist geht es dort nur bis 10ms, DC fehlt, eben weil DC (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent = Gleichstrom = Linearbetrieb) nicht zulässig ist. Manchmal hat der Hersteller auch &amp;quot;vergessen&amp;quot;, die Kennlinie für DC mit reinzuschreiben, wie z.B. bei [http://www.irf.com/product-info/hi-rel/alerts/fv5-p-09-01-A.pdf IRF], wie in diesem [http://www.mikrocontroller.net/topic/291760#3106758 Beitrag] zu erfahren ist.&lt;br /&gt;
Ein recht gutes Indiz dafür, ob ein FET für den Linearbetrieb taugt, ist die Vorwärtssteilheit. Diese kennzeichnet die Abhängigkeit des Drainstromes von der Ansteuerung am Gate als &amp;lt;math&amp;gt;S = \Delta i_d/\Delta u_{gs}&amp;lt;/math&amp;gt;. Moderne Trench-FET erreichen heute Steilheiten im dreistelligen Bereich und sind für Linearanwendungen völlig unbrauchbar. Zum Vergleich: Der BUZ11 kommt mit gerade einmal 4 bis 5 Siemens daher.&lt;br /&gt;
&lt;br /&gt;
In diesem Beitrag wird die DC-Linie im SOA-Diagramm noch genauer erklärt: [http://www.mikrocontroller.net/topic/319961#3473567 Re: MOSFET Linearbetrieb möglich?]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
* [[Mosfet-Übersicht]]&lt;br /&gt;
* [[IGBT]]&lt;br /&gt;
* [[TRIAC]]&lt;br /&gt;
* [[Kühlkörper]] &lt;br /&gt;
* [[Zwischenkreiskapazität]]&lt;br /&gt;
* [[Treiber]]&lt;br /&gt;
* [[Snippets#Wie_schlie.C3.9Fe_ich_einen_MOSFET_an_einen_Mikrocontroller_an.3F|Wie schließe ich einen Mosfet an einen Mikrocontroller an?]]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/H-Br%C3%BCcken_%C3%9Cbersicht Übersicht H-Brücken]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/168218#1609684 Forumsbeitrag]: MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/186785#new Forumsbeitrag]: nochmal MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/319961#3473567 Forumsbeitrag]: sehr ausführlicher Forumsbeitrag über MOSFETs im Linearbetrieb. Berücksichtigt auch den Spirito-Effekt.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/143324#new Forumsbeitrag]: Über eine elektronische Last, sehr lang&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag]: Logic Level MOSFETs direkt mit einem [[AVR]] treiben.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787855 Forumsbeitrag]: MOSFETs im Linearbetrieb, Laborerfahrungen&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787945 Forumsbeitrag]: MOSFETs für Linearbetrieb&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/269642?goto=2820617#2820617 Forumsbeitrag]: Verpol- und Überspannungsschutz mit MOSFETs&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/425414#4981611 Forumsbeitrag]: Linearbetrieb von MOSFETs&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.elektronikinfo.de/strom/feldeffekttransistoren.htm Feldeffekttransistoren bei elektronikinfo.de]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0207011.htm FET im ELKO]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0510161.htm MOSFET im ELKO]&lt;br /&gt;
* [http://www.sprut.de/electronic/switch/nkanal/nkanal.html MOSFET bei sprut.de]&lt;br /&gt;
* [http://sound.westhost.com/articles/hexfet.htm#51 MOSFETs in Audioendstufen, engl.]&lt;br /&gt;
* [http://irf.custhelp.com/cgi-bin/irf.cfg/php/enduser/std_adp.php?p_faqid=214&amp;amp;p_created=1019728945&amp;amp;p_sid=pt9ITiCj&amp;amp;p_accessibility=0&amp;amp;p_redirect=&amp;amp;p_lva=&amp;amp;p_sp=cF9zcmNoPTEmcF9zb3J0X2J5PSZwX2dyaWRzb3J0PSZwX3Jvd19jbnQ9MTQsMTQmcF9wcm9kcz0mcF9jYXRzPSZwX3B2PSZwX2N2PSZwX3BhZ2U9MSZwX3NlYXJjaF90ZXh0PWxpbmVhcg**&amp;amp;p_li=&amp;amp;p_topview=1 FAQ Answer ID 214 bei IRF zum Linearbetrieb]&lt;br /&gt;
* [http://www.nxp.com/documents/application_note/AN11158.pdf AN11158 - Understanding power MOSFET data sheet parameters] von NXP (PDF)&lt;br /&gt;
* [https://assets.nexperia.com/documents/technical-note/TN00008.pdf TN00008 - Power MOSFET frequently asked questions and answers] von nexperia (PDF)&lt;br /&gt;
* [http://www.infineon.com/dgdl/Infineon+-+Application+Note+-+PowerMOSFETs+-+OptiMOS%E2%84%A2+-+Linear+Mode+Operation+and+SOA+Power+MOSFETs.pdf?fileId=db3a30433e30e4bf013e3646e9381200 AN: Linear Mode Operation andSafe Operating Diagram of Power-MOSFETs] von Infineon (PDF)&lt;br /&gt;
* [http://www.ixys.com/Documents/Articles/Article_Linear_Power_MOSFETs.pdf MOSFETs Withstand Stress of Linear-Mode Operation] Neuentwickelte MOSFETs für Linearbetrieb (PDF)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]] [[Kategorie:Leistungselektronik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107024</id>
		<title>FET</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107024"/>
		<updated>2024-07-19T16:28:17Z</updated>

		<summary type="html">&lt;p&gt;Heha: Depletion-Mode-Ergänzung Verfügbarkeit&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel versteht sich als Unterpunkt zum Artikel [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein FET (engl. &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor) ist ein  Feldeffekttransistor. Der FET ist ein Bauelement, das im Gegensatz zum Bipolartransistor (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT) mit Spannung und nicht mit Strom gesteuert wird. Unterschieden werden&lt;br /&gt;
* MOSFET = engl. &#039;&#039;&#039;M&#039;&#039;&#039;etall &#039;&#039;&#039;O&#039;&#039;&#039;xide &#039;&#039;&#039;S&#039;&#039;&#039;emiconductor &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor; Metalloxidschicht-FET, größte Teilgruppe der FETs mit isoliertem Gate &lt;br /&gt;
* JFET = engl. &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, Übergangszonen FET, der steuerbare Kanal wird durch einen PN-Übergang wie in einer Diode gebildet&lt;br /&gt;
&lt;br /&gt;
Die drei Anschlüsse eines FETs werden &#039;&#039;Gate&#039;&#039;, &#039;&#039;Drain&#039;&#039; und &#039;&#039;Source&#039;&#039; genannt. Unter Umständen ist ein vierter Anschluß vorhanden, der &#039;&#039;Bulk&#039;&#039; genannt wird. Normalerweise ist Bulk intern mit Source verbunden. Wenn dies nicht der Fall ist, muss diese Verbindung durch den Designer in der Schaltung hergestellt werden.&lt;br /&gt;
&lt;br /&gt;
== FET-Typen ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
FETs werden hauptsächlich unterschieden in N-Kanal und P-Kanal, sowie &amp;quot;selbst sperrend = Anreicherungstyp&amp;quot; (engl. enhancement type) und &amp;quot;selbst leitend = Verarmungstyp&amp;quot; (engl. depletion type). Beim selbstleitenden FET ist der Transistor bei 0V Gate-Source Spannung maximal leitend (durchgesteuert) und wird durch Anlegen einer Spannung ans Gate gesperrt. Beim selbstsperrenden FET (größte Gruppe) ist der Transistor bei 0V Gate-Source Spannung gesperrt und wird durch Anlegen einer Spannung ans Gate leitend. Ist die Linie zwischen Drain und Source durchgezogen handelt es sich um einen selbstleitenden, bei einer gestrichelten Linie um einen selbstsperrenden FET. JFETs gibt es nur als Verarmungstyp. Im weiteren Artikel wird nur mehr der &amp;quot;selbstsperrende&amp;quot; MOSFET betrachtet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Typen von Feldeffekttransistoren&amp;lt;br/&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Typ &lt;br /&gt;
! N-Kanal &lt;br /&gt;
! P-Kanal&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| JFET&amp;lt;br/&amp;gt;SFET&amp;lt;br/&amp;gt;(selbst leitend) || [[bild:JFET-N.png|center]]&lt;br /&gt;
* drittgrößte Gruppe&lt;br /&gt;
* bislang nur für kleine Leistungen verfügbar&lt;br /&gt;
* JFETs mit hoher Leistung sind im Kommen&lt;br /&gt;
* Eingangsstufen von OPVs&lt;br /&gt;
* Eingangsstufen von HF-Verstärkern bis in den GHz-Bereich&lt;br /&gt;
** Typen mit zwei Gates zum Regeln und Mischen üblich&lt;br /&gt;
* als einfache [[Konstantstromquelle]] geeignet&lt;br /&gt;
| [[bild:JFET-P.png|center]]&lt;br /&gt;
* selten&lt;br /&gt;
* ersatzhalber für „fehlenden“ P-Kanal-Verarmungs-MOSFET&lt;br /&gt;
* Ältester Vertreter der Unipolartransistoren&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Anreicherungstyp&amp;lt;br/&amp;gt;(selbst sperrend)&amp;lt;br/&amp;gt;&#039;&#039;Enhancement Mode&#039;&#039; || [[bild:MOS-EN.png|center]]&lt;br /&gt;
* Mit Abstand größte Gruppe; wenn nicht besonders erwähnt ist &#039;&#039;dieser&#039;&#039; gemeint&lt;br /&gt;
* sehr viele Typen erhältlich&lt;br /&gt;
* Auch als Doppel-MOSFET oder zusammen mit P-Kanal-MOSFET in einem Gehäuse gängig&lt;br /&gt;
| [[bild:MOS-EP.png|center]]&lt;br /&gt;
* zweitgrößte Gruppe&lt;br /&gt;
* bei gleicher Geometrie etwas schlechter als ein N-Kanal Typ&lt;br /&gt;
* Auch als Doppel-MOSFET oder zusammen mit N-Kanal-MOSFET in einem Gehäuse gängig&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Verarmungstyp&amp;lt;br/&amp;gt;(selbst leitend)&amp;lt;br/&amp;gt;&#039;&#039;Depletion Mode&#039;&#039; || [[bild:MOS-DN.png|center]]&lt;br /&gt;
* selten aber käuflich&lt;br /&gt;
* Sinnvolle Anwendungen ergeben sich vor allem in Reihenschaltungen&lt;br /&gt;
| [[bild:MOS-DP.png|center]]&lt;br /&gt;
* Nicht in freier Wildbahn gesichtet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Vorteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Niedrigere Verluste als bei Bipolartransistoren.&lt;br /&gt;
* Sehr schnelles Schalten möglich, daher für sehr hohe Frequenzen geeignet (keine Speicherzeit wie beim BJT).&lt;br /&gt;
* Einfaches Parallelschalten im Schaltbetrieb, da Unterschiede im &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; durch den positiven Temperaturkoeffizienten ausgeglichen werden.&lt;br /&gt;
* Leistungslose Ansteuerung im statischen Fall, jedoch hohe Umladeverluste am Gate!&lt;br /&gt;
* oft preiswerter als vergleichbare Bipolartransistoren (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT)&lt;br /&gt;
* Relativ unempfindlich gegen Überspannung zwischen Drain und Source. Bei Überschreitung der Maximalspannung zwischen Drain und Source findet ein sogenannter &amp;quot;Durchbruch&amp;quot; statt. Dies ist vergleichbar mit dem Zener-Effekt. Ist die Energiemenge begrenzt, ist dieser Durchbruch reversibel und der FET wird NICHT zerstört. Beim Bipolartransistor ist die dafür zulässige Energiemenge viel kleiner, weil die den Strom durchquerende Fläche zu Hotspots neigt.&lt;br /&gt;
&lt;br /&gt;
=== Nachteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Nur bedingt für hohe Spannungen [[Transistor#Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein ? |geeignet]], die ON-Verluste sind ab ca. 250V höher als bei einem [[IGBT]]. &lt;br /&gt;
* Parasitäre Diode parallel zur Drain-Source Strecke ist immer enthalten, das (Ab-)Schaltverhalten dieser Dioden ist meist schlechter als separate Dioden, was häufig zu unerwünschten Schwingungen führt.&lt;br /&gt;
* Empfindlicher gegen ESD am Gate als BJT&lt;br /&gt;
* Positiver Temperaturkoeffizient (TK), der &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; ist stark temperaturabhängig und steigt von 25°C (Datenblattangabe) auf 150°C ungefähr um den Faktor 2. Dadurch steigen auch die Verluste und damit die Erwärmung des Bauteiles.&lt;br /&gt;
&lt;br /&gt;
=== Erklärung der wichtigsten Datenblattwerte ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:7em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | Beispiel&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Drain Source Voltage &amp;lt;br&amp;gt;(Breakdown) || V(BR)_DSS &amp;lt;br&amp;gt; V_DS || 75 V || Maximale Spannungsfestigkeit des Bauteiles zwischen Drain und Source&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Drain Current  || I_D(on)   || 55 A @ 125 °C  || Maximaler Dauerstrom bei 125°C Gehäusetemperatur &lt;br /&gt;
|-&lt;br /&gt;
| Pulsed Drain Current || ID_pulse &amp;lt;br&amp;gt; I_CD(on) || 240 A || Maximaler Pulsstrom (Achtung die zulässige Zeitdauer des Pulses kann nur über die maximale Junctiontemperatur ermittelt werden)&lt;br /&gt;
|-&lt;br /&gt;
| Gate Charge || Q&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; ||  320 nC || Gate-Ladung in Coloumb oder Amperesekunden, die zum Ein- oder Ausschalten des MOSFET erforderlich ist. Dies berücksichtigt die Nichtlinearität der Kapazität und (richtig?) den Miller-Effekt. Mit dieser Angabe wird der für das Schalten von Leistungs-MOSFET benötigte Gatestrom für eine bestimmte Schaltzeit ermittelt. Dieser liegt üblicherweise im einstelligen Ampere-Bereich. Moderne Typen mit extra geringer Gate-Ladung können so schneller schalten.&lt;br /&gt;
|-&lt;br /&gt;
| Repetetive Avalanche Energy || t_sc ||  280 mJ || Maximale Energie, welche beim Avalanche Durchbruch bei Überschreiten der maximalen Drain-Source Spannung im MOSFET bei z.&amp;amp;nbsp;B. 1% Puls/Pausen Verhältnis regelmäßig auftreten darf, ohne den FET zu schädigen&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_ON&amp;lt;/sub&amp;gt; ||  0,01 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;25°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_on&amp;lt;/sub&amp;gt; ||  0,021 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;175°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Thermal Resistance&amp;lt;br&amp;gt;(junction-case) ||  R&amp;lt;sub&amp;gt;th_JC&amp;lt;/sub&amp;gt; ||  0,8 K/W || Thermischer Widerstand im Transistor vom der „Sperrschicht“ (junction eines Bipolartransistors, ja altmodischer Index, hier besser „Kanal“) bis zur Rückseite oder bestmöglichen Kühlmöglichkeit des Transistorgehäuses (case)&lt;br /&gt;
|-&lt;br /&gt;
| Gate-Source&amp;lt;br&amp;gt;Threshold Voltage ||  V_GS(th) || 2,0-4,5 V || Gatespannung, ab welcher der Transistor minimal leitend wird (I_D typisch 100-200µA), große Toleranz, typisch 1:2 zwischen Minimum und Maximum&lt;br /&gt;
|-&lt;br /&gt;
| Turn-on Delay ||  t_d(on) || 40 ns ||  Verzögerung zwischen dem Einschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Rise Time ||  t_r || 200 ns ||  Anstiegszeit des Transistorstromes am Drain&lt;br /&gt;
|-&lt;br /&gt;
| Turn-off Delay || t_d(off) || 120 ns ||  Verzögerung zwischen Abschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Fall Time ||  t_f  || 60 ns || Abfallzeit des Transistorstromes am Drain  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die oben genannten Zeiten gelten ausschließlich unter den angegebenen Messbedingungen (Gatewiderstand, Treiberspannung, sowie einer &#039;&#039;&#039;FET-Teperatur von 25°C!&#039;&#039;&#039;) und müssen für die eigene Anwendung ggf. neu berechnet werden. Meist wird man sie eher messen, weil die Rechung zu aufwändig und bisweilen unmöglich ist. &lt;br /&gt;
&lt;br /&gt;
==== Gate-Source Threshold Voltage ====&lt;br /&gt;
Gerade bei der &#039;&#039;&#039;Gate-Source Threshold Voltage &amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt;&#039;&#039;&#039; gibt es hier immer wieder Verwirrung. Sie gibt an, ab welcher Spannung der MOSFET anfängt, minimal leitfähig zu werden. Diese Spannung ist technologisch bedingt auch heute noch einer starken Toleranz unterworfen, typischerweise hat der Bereich  bei dem der FET zu leiten beginnt eine Spreizung von etwa 1:2 zwischen Minimum und Maximum. Welche Spannung man nun wirklich anlegen muss, um den gewünschten &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt; zu erreichen kann der Tabelle im jeweiligen Datenblatt entnommen werden. Dabei unbedingt die angegebene Gatespannung beachten, nur dieser Wert ist garantiert!.&lt;br /&gt;
&lt;br /&gt;
[[bild: IRLZ34N_R_DS_ON.png | thumb | 800px | R_DS_ON im Datenblatt des IRLZ34N]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Die Kurvenschar von &amp;lt;math&amp;gt;I_D&amp;lt;/math&amp;gt; über &amp;lt;math&amp;gt;U_{DS}&amp;lt;/math&amp;gt; in Abhängigkeit von &amp;lt;math&amp;gt;U_{GS}&amp;lt;/math&amp;gt; stellt immer nur typische Werte dar, keine garantierten Extremwerte (engl. worst case). Als Standardwerte kann man 10-15V für einen Standardtypen und ca. 3-5V für einen Logic Level MOSFET (LL-FET) ansetzen. Kleinsignal-FETs leiten schon ab ca 1V. Bei Ansteuerung mit 5V benötigt man also einen Typen, der &#039;&#039;&#039;sicher&#039;&#039;&#039; bei 5V voll durchgesteuert ist, z.B. IRLZ34N. Bei 3,3V ist er bereits nicht mehr zuverlässig nutzbar. Es gibt auch Typen mit noch geringerer Spannung für Vollaussteuerung. Wer einen BUZ11 (&amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt; 4V max.) mit 5V ansteuert riskiert ein Abfackeln des MOSFETs, denn je nach Toleranz kann er bereit ganz gut aufgesteuert sein oder auch nicht. Das soll in den nachfolgenden Diagrammen dargestellt werden. Zunächst die Transferkennlinie. Sie gibt an, wieviel Drainstrom in Abhängigkeit der Gatespannung fließen kann, wobei die Drainspannung konstant ist, hier im Beispiel 25V. Ein typischer BUZ11 (mittlere, schwarze Kurve) fängt bei 3V zu leiten an und erreicht bei 6V am Gate ca. 17A. Erwischt man nun ein kritisches Exemplar, das erst bei 4V zu leiten anfängt (rechte, rote Kurve um 1V parallel verschoben), so kann dieser bei 6V nur 8A leiten, für 17A braucht er 7V. Der günstige Fall, daß der BUZ11 schon bei 2V anfängt zu leiten sieht man in der linken, roten Kurve.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UGS_ID.png | thumb | 800px | Transferkennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Wenn man die drei Fälle (min, typ, max) mit 5V ansteuert, erhält man die Arbeitspunkte 1-3 mit einem maximal schaltbaren Drainstrom von 2,5, 8 und 17A. Diese kann man in das Ausgangskennlinienfeld übertragen. Wir nehmen max. 1V U_DS an. Es ergeben sie die gleichen Ströme. Der maximale Drainstrom weicht um -5,5/+9A vom typischen Fall ab. Erhöht man nun die Gate-Source Spannung auf 10V (Arbeitspunkt 4), schwankt der maximal schaltbare Drainstrom nur noch um -2/+2A, außerdem liegt er mit 30A deutlich höher. Der BUZ11 ist somit sicher durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UDS_ID_UGS.png | thumb | 800px | Ausgangskennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
==== Parasitäre Diode ====&lt;br /&gt;
&lt;br /&gt;
Der Schwerpunkt in der FET-Entwicklung liegt auf den geringst-möglichen &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt;,  die Diode entsteht auf Grund des Herstellungsprozesses, und wird nur nachrangig verbessert, da viele Optimierungsversuche auch einen Einfluss auf wichtige Kennwerte des FETs hatten und haben.&lt;br /&gt;
Daher muss sorgfältig geprüft werden, ob die Schaltgeschwindigkeit, die Recovery-Time und die damit verbundenen Verluste sowie die dabei erzeugte unerwünschte EMV-Abstahlung tolerierbar ist, oder nicht.&lt;br /&gt;
Hier hilft es oft eine optimierte Diode / Schottky-Diode zum FET parallel zu schalten. Ganz ausblenden läßt sich die parasitäre Diode jedoch nicht, jedoch kann man den Anteil des Stromes beeinflussen, der über die intere Diode fliest.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039; Parasitäre Diode des FETs  &#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Current  ||  I_S || 75A || Maximaler Dauerstrom der parasitären Diode, meist identisch zum maximalen Dauerstrom des MOSFETs&lt;br /&gt;
|-&lt;br /&gt;
| Forward Voltage ||  V_SD || 1,3V || Spannungsfall an der parasitären Diode &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Time ||  t_rr || 120ns || Zeit, welche die Elektronen brauchen um aus der leitenden Diode vollständig abzufließen. Während dieser Zeit fließt der Strom in &#039;&#039;&#039;Rückwärtsrichtung&#039;&#039;&#039; durch die Diode und erzeugt relativ viel Verlustleistung. &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Charge ||  Q_rr || 60nC || Ladungsmenge, die während t_rr rückwärts durch die Diode fließt.  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Haupttypen und Gatespannungslevel ==&lt;br /&gt;
&lt;br /&gt;
===Unterschied N-Kanal / P-Kanal FET===&lt;br /&gt;
&lt;br /&gt;
Im Schaltsymbol werden die MOSFET-Typen meist durch die Pfeilrichtung in der Mitte des Symbols (eigentlich &amp;quot;Bulk&amp;quot;) vom oder zum Gate unterschieden.  Zeigt der Pfeil zum Gate hin, handelt es sich um einen N-Kanal-FET, zeigt der Pfeil vom Gate weg um einen P-Kanal FET.&lt;br /&gt;
&lt;br /&gt;
Der große Vorteil des N-Kanal FETs (Elektronenleitung) ist, daß er immer niederohmiger ist, als ein gleich großer P-Kanal FET (Löcherleitung). Daher sind P-Kanal Typen bei vergleichbaren Werten auch immer größer = teuerer da weniger Chips auf einem Wafer Platz haben.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET muss die Gatespannung positiv gegenüber Source sein. Dabei wird der FET dann leitend, wenn die sogenannte &amp;quot;threshold voltage&amp;quot; (Schwellenspannung) erreicht wird. Eine typische Anwendung ist z.&amp;amp;nbsp;B. ein &#039;&#039;&#039;Low-Side Schalter&#039;&#039;&#039;: Source an GND, Drain an die Last, Ansteuerung des N-Kanal FETs mit 12V gleichbedeutend mit 12V ÜBER den Source = GND Potential.&lt;br /&gt;
 &lt;br /&gt;
Beim P-Kanal FET als HS-Schalter muss die Gatespannung negativer=niedriger als das Sourcepotential sein.Beispiel.&lt;br /&gt;
Beispiel:  &lt;br /&gt;
Lastspannung = 400V d.h. Source an 400V, Last zwischen Drain und GND, Ansteuerung des P-Kanal FETs mit 388V, also 12V UNTER dem Sourcepotential.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET als HS-Schalter muss die Gatespannung positver=höher als das Sourcepotential sein.&lt;br /&gt;
Beispiel:&lt;br /&gt;
Lastspannung = 400V d.h. Drain an 400V, die Last zwischen Source und GND, Ansteuerung des N-Kanal FETs mit 412V, also 12V ÜBER dem Sourcepotential.&lt;br /&gt;
In diesem Fall ist aber eine zusätzliche Spannungsquelle erforderlich, denn der FET wird mit einer Spannung über der Lastspannung eingeschaltet. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weblinks&#039;&#039;&#039;&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-1-of-2/?utm_source=eetimes&amp;amp;utm_medium=networksearch A primer on high-side FET load switches (Part 1 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products, Micrel, Inc., 5/3/2007 4:14 PM EDT, www.eetimes.com&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-2-of-2/ A primer on high-side FET load switches (Part 2 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products. Micrel, Inc., 5/7/2007 1:36 PM EDT, www.eetimes.com&lt;br /&gt;
* [http://www.vishay.com/docs/70611/70611.pdf AN804 P-Channel MOSFETs, the Best Choice for High-Side Switching (PDF)] von Vishay Siliconix&lt;br /&gt;
&lt;br /&gt;
===Unterschied Logic-Level / &amp;quot;Normal&amp;quot;-Level===&lt;br /&gt;
&lt;br /&gt;
Den meisten FETs ist gemein, daß sie mit einer Spannung von 10..15V angesteuert werden müssen, um den minimalen Einschaltwiderstand zu erreichen. Diese FETs lassen sich nicht ohne weiteres mit einem CMOS-Pegel von 5V ansteuern. Es gibt jedoch für diesen Anwendungsfall sogenannte &amp;quot;Logic Level&amp;quot; (LL) FETs, die schon bei einer Gatespannung von etwa 4,5V voll durchgesteuert sind. Einige Kleinsignal-FETs sind schon ab ca. 1,2V voll durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
== Beispiel zur Bauteiledimensionierung ==&lt;br /&gt;
&lt;br /&gt;
=== Spannungsfestigkeit ===&lt;br /&gt;
&lt;br /&gt;
Die höchste vorkommende Betriebsspannung + Abschaltüberspannung soll kleiner als ca. 80% der Spannungsfestigkeit des Bauteiles sein. &lt;br /&gt;
&lt;br /&gt;
Achtung: Zwischen dem je nach Anwendungsfall erforderlichen Pufferkondensator und dem FET wird es immer eine parasitäre Induktivität geben.&lt;br /&gt;
Abhängig von Schaltgeschwindigkeit und Induktivität wird im Schaltmoment eine mehr oder weniger große Übrspannungsspitze produziert. Dieser Peak&lt;br /&gt;
addiert sich auf die aktuelle Versorgungsspannung.&lt;br /&gt;
&lt;br /&gt;
Überschlagsrechnung als Beispiel:&lt;br /&gt;
* Schaltgeschwindigkeit:  dI/dt = -100A/µs (= Abschalten von 5A innerhalb 50ns),&lt;br /&gt;
* Induktivität:   L = 1µH (~ 1 m loses, ungebündeltes Kabel)&lt;br /&gt;
* dU=-L*dI/dt = -1µH * (-100A / 1µs) = 100V&lt;br /&gt;
&lt;br /&gt;
Dies bedeutet, daß an der &amp;quot;Induktivität&amp;quot; zwischen Transistor und Kondensator - Aufgrund von Selbstinduktion im Schaltmoment - ein Überspannungspuls von ca. 100V entsteht, der auf die Betriebsspannung aufzuschlagen ist, also lieber die Leitung kürzer machen, und - sofern möglich - nicht ganz so schnell schalten.&lt;br /&gt;
&lt;br /&gt;
=== Stromtragfähigkeit ===&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt ist eine Stromtragfähigkeit bei 25°C, und meist noch bei einer höheren Temperatur z.B. 125°C, 150°C oder 175°C Kühlfahnentemperatur angegeben. Dieser Wert ist als ERSTE Entscheidungsgrundlage ausreichend, aber aus der theoretisch abführbaren Verlustleistung errechnet, und&lt;br /&gt;
* dient zum qualitativen Vergleich von Transistoren bezüglich ihres R_ds(on) und ihres Wärmewiderstands.&lt;br /&gt;
* ist für die Dimensionierung einer Schaltung nur als Richtwert zu interpretieren. &lt;br /&gt;
* ist ohne Schaltverluste genannt, und daher nur für einen Schaltbetrieb von wenigen Hz gültig. Außerdem wird ein annähernd idealer Kühlkörper unterstellt, der trotz der Verlustleistung das Gehäuse des Transistors auf der angegebenen Temperatur halten kann.&lt;br /&gt;
* entbindet einen nicht davon den Kopf einzuschalten... siehe die nachfolgenden Zeilen.&lt;br /&gt;
* Liegt der Strom für den die Schaltung entwickelt wird mit ca. 10..20% Abstand unter dem Datenblattwert von 125°C ist dieses Bauteil vermutlich verwendbar (siehe Detailberechnungen unten !).   &lt;br /&gt;
* Ist der benötigte Strom im Bereich oder größer als der zulässige bei 125°C sollte entweder ein anderer Typ eingesetzt oder mehrere FETs &#039;&#039;des gleichen Typs&#039;&#039; parallelgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
== Verlustleistung ==&lt;br /&gt;
&lt;br /&gt;
Hier wird eine Näherung für eine getaktete Anwendung betrachtet. In einem Transistor treten sowohl beim Ein- und Ausschalten, als auch während der Einschaltphase Verluste im Bauteil auf. Diese Verluste führen zu einer Bauteilerwärmung. Die dabei entstehende Temperatur darf die maximal zulässige Bauteiletemperatur nie überschreiten. Bei den ersten Projekten ist zu empfehlen eine berechnete Chiptemperatur von ca. 125°C nicht zu überschreiten. Fast alle aktuell verfügbaren FETs nennen im Datenblatt eine Temperatur von 175°C als ihre maximale Chiptemperatur.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:32em&amp;quot; &lt;br /&gt;
|+ &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Symbol&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Wert&lt;br /&gt;
|-&lt;br /&gt;
| Betriebsspannung || U&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 70 V&lt;br /&gt;
|-&lt;br /&gt;
| Nennstrom || I&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 30 A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source Widerstand bei&amp;lt;br&amp;gt;Chiptemperatur: 125°C&amp;lt;br&amp;gt; Gatespannung: 10V || R&amp;lt;sub&amp;gt;DS&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt;&amp;lt;/sub&amp;gt; || 17 mΩ&lt;br /&gt;
|-&lt;br /&gt;
| Pulsbreite || t&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt; || 150 µs&lt;br /&gt;
|-&lt;br /&gt;
| Schaltfrequenz || ƒ&amp;lt;sub&amp;gt;schalt&amp;lt;/sub&amp;gt; || 5 kHz&amp;lt;br&amp;gt;T = 200µs&lt;br /&gt;
|-&lt;br /&gt;
| Einschaltzeit (risetime) || t&amp;lt;sub&amp;gt;r&amp;lt;/sub&amp;gt; || 500 ns&lt;br /&gt;
|-&lt;br /&gt;
| Ausschaltzeit (falltime) || t&amp;lt;sub&amp;gt;ƒ&amp;lt;/sub&amp;gt; || 800 ns &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Leitend-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Während der FET bei [[PWM]]-Ansteuerung eingeschaltet ist, erzeugt er Verlustleistung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
P_\text{ON}&lt;br /&gt;
 = {I_\mathrm{N}}^2 \cdot R_\mathrm{DS_\mathrm{ON}} \cdot \frac{t_\mathrm{ON}}{T}&lt;br /&gt;
 = 30^2A^2 \cdot 17m\Omega  \cdot \frac{150\mu s}{200\mu s} = 11{,}5W&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schalt-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Vereinfachter Ansatz.&lt;br /&gt;
&lt;br /&gt;
Einschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_r}&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_r}{T} \\&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot 70V \cdot 30A \cdot \frac{500ns}{200\mu s}=1{,}3W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_f} &lt;br /&gt;
&amp;amp;=\tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_f}{T}\\&lt;br /&gt;
&amp;amp;=\tfrac14 \cdot 70V \cdot 30A \cdot \frac{800ns}{200\mu s}=2{,}1W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ und genauer kann man rechnen, wenn die Ein- Ausschaltenergie im Datenblatt angegeben ist. Aber Achtung! Die  Randbedingungen unter denen die genannte Energie ermittelt wurde, müssen genau so zutreffen.&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_f} = f_{schalt} \cdot E_{ON}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_r} = f_{schalt} \cdot E_{OFF}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Gesamtverlustleistung beträgt also in etwa 15W.&lt;br /&gt;
&lt;br /&gt;
Damit muß ein entsprechender [[Kühlkörper]] ausgelegt und die Chiptemperatur berechnet werden. z.&amp;amp;nbsp;B.:&lt;br /&gt;
* Kühlkörper mit einem R_th von 0,2K/W&lt;br /&gt;
* max. Umgebungstemperatur +60°C&lt;br /&gt;
* R_th &amp;quot;junction-case&amp;quot; des FETs 0,8K/W&lt;br /&gt;
* R_th der Wärmeleitfolie zwischen FET und Kühlkörper ca. 2,0K/W&lt;br /&gt;
* R_th gesamt: 3,0K/W &amp;lt;br&amp;gt;&lt;br /&gt;
* Bei einer Verlustleistung von 18W und einer Umgebungstemperatur von 60°C hat der Chip eine Temperatur von ca. 18W * 3,0K/W +60°C = 114°C. ==&amp;gt; o.k.!&lt;br /&gt;
&lt;br /&gt;
Unter Berücksichtigung der Tatsache, daß hier viele Vereinfachungen vorgenommen, und die Art der Last nicht beachtet wurde ist es sinnvoll, einen gewissen Sicherheitsabstand zu den zulässigen Maximalwerten einzuhalten. Daher ist es empfehlenswert, die Chiptemperatur auf ca. 125°C zu beschränken. &lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist hier die parasitäre Diode im FET nicht berücksichtigt.&lt;br /&gt;
Wenn während der &amp;quot;off&amp;quot; Zeit ein Strom über die Diode fließt (Reverse recovery current oder Freilaufstrom), muß die dadurch &#039;&#039;&#039;zusätzlich&#039;&#039;&#039; entstehende Verlustleistung in die obige Berechnung der maximalen Chiptemperatur mit einfließen.&lt;br /&gt;
&lt;br /&gt;
==Treiberleistung==&lt;br /&gt;
&lt;br /&gt;
Auch wenn der MOSFET ein spannungsgesteuertes Bauelement ist, muss trotzdem bei jedem Einschalten und bei jedem Ausschalten die Gatekapazität umgeladen werden. Bei älteren Leistungs-FET - oder bei einem schlechten Design (!) - muss sogar teilweise mit negativer Spannung am Gate gearbeitet werden, um eine vollständige Sperrung zu erreichen.&lt;br /&gt;
Diese Umladung muss möglichst schnell erfolgen, um die Verluste im FET während der Umschaltphase zu minimieren. Dazu findet ein [[Mosfet-Übersicht#MOSFET-Treiber|MOSFET-Treiber]] Verwendung. Hier eine detaillierte Beschreibung zum [[Treiber]].&lt;br /&gt;
&lt;br /&gt;
Da die Gatekapazität nicht direkt im Datenblatt enthalten ist kann man sich mit der Eingangskapazität Ciss behelfen. Im Arbeitspunkt ist die Gatekapazität ungefähr 5x größer als der im Datenblatt für Ciss angegebene Wert. &lt;br /&gt;
Daher berechnet sich die Treiberleistung wie folgt: &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{Treiber} = C \cdot U^2 \cdot f = 5 \cdot C_\text{íss} \cdot U_\text{Gate}^2 \cdot f_\text{schalt}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1.Beispiel, kleine MOSFET-Steuerung mit niedriger Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 4{,}8\,\text{nF} \cdot 15\,\text{V}^2 \cdot 10\,\text{kHz} = 54\,\text{mW}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2.Beispiel, sehr große MOSFET-Steuerung für Induktionsheizung mit sehr hoher Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 24\,\text{nF} \cdot 15\,\text{V}^2 \cdot 250\,\text{kHz} = 6{,}75\,\text{W}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung, so ein MOSFET-Treiber hat auch einen Eigenverbrauch, der leicht zwischen 0,5 und 1 W liegen kann.&lt;br /&gt;
&lt;br /&gt;
Bei niedrigen PWM-Frequenzen kann man Logic Level MOSFETs auch direkt per CMOS-Ausgang ansteuern, z.B. mit einem [[AVR]], wie in diesem [http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag] zu sehen ist.&lt;br /&gt;
&lt;br /&gt;
== Low- und High-Side ==&lt;br /&gt;
&lt;br /&gt;
Definition LS- und HS:&lt;br /&gt;
 &lt;br /&gt;
;Low-Side Schalter: Der FET schaltet eine Last gegen GND - auch als LS-Schalter bezeichnet.&lt;br /&gt;
;High-Side Schalter: Der FET schaltet eine Last an die Versorgungsspannung – auch als HS-Schalter bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Anregungen oder Fragen auch gerne per Email an [http://www.mikrocontroller.net/user/show/powerfreak Powerfreak]. Dieser Artikel kann dadurch regelmäßig erweitert und ggf. durch ein FAQ ergänzt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== SOA Diagramm ==&lt;br /&gt;
&lt;br /&gt;
SOA-Diagramm (engl. &#039;&#039;&#039;S&#039;&#039;&#039;afe &#039;&#039;&#039;O&#039;&#039;&#039;perating &#039;&#039;&#039;A&#039;&#039;&#039;rea, sicherer Arbeitsbereich) beschreibt die zulässige Verlustleistung eines Transistors in Anhängigkeit des Drainstroms (I_D), der Drain-Source Spannung (U_DS) und der Pulsbreite. Als Beispiel sei hier der BUZ 11 genannt. Im nachfolgenden Diagramm ist das SOA-Diagramm dargestellt. Wie ist es zu verstehen? Zunächst gibt es eine Grenze auf der linken Seite, die schräge, dunkelblaue Line. Diese wird durch den minimalen R_DS_ON festgelegt, hier wirkt der MOSFET wie ein ohmscher Widerstand. Mehr Strom kann bei einer bestimmten Spannung nicht fließen. Die zweite Grenzlinie ist ganz rechts die pinkfarbene Linie, sie stellt die maximale Sperrspannung des MOSFET dar. Die dritte Grenze ist der maximal zulässige Drainstrom, hier im Beispiel 120A, dargestellt durch die gelbe Linie. Die maximale Spannung zwischen Drain und Source sowie der Drainstrom sind abhängig von der Pulsbreite, mit welcher der MOSFET betrieben wird. Bei nur 2,5µs Pulsbreite (Rechteckimpuls) müssen die beiden Parameter sich innerhalb der Fläche bewegen, welche durch die dunkelblaue, gelbe und die pinkfarbene Line begrenzt wird. Im Extremfall dürfen 50V anliegen und 120A fließen, das sind satte 6kW Pulsleistung! Werden die Pulse breiter, so sinken die zulässigen Ströme und Spannungen, bei 1ms (dunkelblaue Linie bis zur braunen Linie, dann zur pinkfarbenen Linie) sind maximal noch 50V und 7A zulässig, also nur noch 350W. Die letzte Linie stellt den Fall für Gleichstrom (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent), also Dauerbelastung dar, hier sind bei 50V maximal 1,5A zulässig, was einer Dauerverlustleistung von 75W entspricht. MOSFETs, welche nur für Schaltbetrieb und nicht für [[#Linearbetrieb von MOSFETs | Linearbetrieb]] geeignet sind, haben keine Kennlinie für DC. Im normalen Schaltbetrieb liegt der Arbeitspunkt auf der linken Grenzlinie R_DS_ON_MIN. Nur im Linearbetrieb liegt der Arbeitspunkt innerhalb der Fläche, welche durch die Außenlinien begrenzt wird.&lt;br /&gt;
&lt;br /&gt;
[[bild: SOA-BUZ11.png | thumb | 300px| SOA-Diagramm]]&lt;br /&gt;
&lt;br /&gt;
Bei der Anwendung des Diagramms gilt es einiges zu beachten. Die Pulsleistungen sind nur zulässig, wenn der MOSFET vorher kalt ist, sprich ca. 25°C Sperrschichttemperatur hat. War er vorher schon heiß, reduziert sich die zulässige Belastung deutlich. Ebenso dürfen die Pulse nicht zu schnell wiederholt werden, denn dann ist der MOSFET noch vom vorherigen Puls aufgeheizt. Im Fall von DC sind 75W Verlustleistung auch eher ein theoretischer Wert, welcher real nur schwer erreicht werden kann, wenn der MOSFET auf einem sehr großen [[Kühlkörper]] optimal montiert ist. Praktisch liegen die erreichbaren Werte eher bei der Hälfte.&lt;br /&gt;
&lt;br /&gt;
(Anm. Eigentlich müsste für die R_DS_ON Grenzlinie R = U / I der minimale R_DS_ON rauskommen, hier ~40mOhm, es kommen aber ~80mOhm raus. Die Ursache dafür ist unklar, möglicherweise liegt hier ein Sicherheitsfaktor zu grunde).&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Linearbetrieb von MOSFETs ==&lt;br /&gt;
&lt;br /&gt;
Der Großteil der Anwendungen nutzt MOSFETs als Schalter, d.h. der MOSFET ist entweder voll gesperrt oder voll durchgesteuert. Dafür gelten auch all die Hinweise in diesem Artikel. In bestimmten Anwendungen werden MOSFETs aber auch im Linearbetrieb eingesetzt, z.B in linearen Endstufen für Audio, Video, elektronischen Lasten und Stromquellen. Hier muss man einiges beachten. Ein verbreiteter Irrtum besteht darin zu glauben, MOSFETs könne man im Linearbetrieb einfach parallel schalten, weil der positive Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; eine Symmetrierung bewirkt, ähnlich den Emitterwiderständen bei parallelgeschalteten Bipolartransistoren. Das ist &#039;&#039;ausschließlich&#039;&#039; im Schaltbetrieb möglich, und daher falsch! Im Linearbetrieb spielt der Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; keine Rolle, weil der MOSFET selten bis nie komplett durchgesteuert ist. Eben darum ist beim Linearbetrieb der minimale &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; in den meisten Fällen unwichtig und man kann auch eher hochohmige, ältere MOSFETs verwenden, wie z.B. den BUZ11.&lt;br /&gt;
&lt;br /&gt;
Hier wirkt vielmehr der negative Temperaturkoeffizient (TK) der Thresholdspannung &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, vergleichbar dem negativen TK der Basis-Emitter-Spannung von Bipolartransistoren. D.h. mit steigender Temperatur und konstanter Gate-Source-Spannung steigt der Stromfluss der Drain-Source Strecke. In einer Parallelschaltung von MOSFETs würde dies bedeuten, dass der MOSFET mit dem geringfügig größeren Drainstrom (Fertigungstoleranzen) wärmer wird, was zu einem weiter steigenden Drainstrom und damit noch mehr Wärme führt. Damit ist die Schaltung thermisch instabil und würde zum Durchbrennen der MOSFETs führen, einer nach dem Anderen. &lt;br /&gt;
&lt;br /&gt;
Um das zu verhindern muss man relativ große Ausgleichswiderstände in die Source-Leitung der einzelnen MOSFETs schalten, um diese Drift zu kompensieren. Dadurch verschlechtert sich natürlich der Wirkungsgrad des Verstärkers. MOSFETs haben einen TK von typisch -5mV/K für &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, das ist mehr als das doppelte von Bipolartransistoren mit typisch -2mV/K, weshalb die Symmetrierungswiderstände mehr als doppelt so groß sein müssen. Weiterhin muss man beachten, dass die Toleranzen von &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt; sehr groß sind, im Bereich von Volt! Das kann man sinnvoll nicht mehr mit Gatewiderständen symmetrieren, hier muss man die MOSFETs ausmessen und Gruppen mit geringen Toleranzen in einer Schaltung verwenden (engl. matching). &lt;br /&gt;
&lt;br /&gt;
Eine andere Möglichkeit ist die getrennte Ansteuerung der einzelnen MOSFETs, das wird oft in elektronischen Lasten bzw. [[Konstantstromquelle#Konstantstromquelle mit Operationsverstärker und Transistor | Konstantstromquellen]] gemacht. Hier treten keine zusätzlichen Verluste auf und der Mehraufwand in der Ansteuerung ist meist unkritisch.&lt;br /&gt;
&lt;br /&gt;
Weiterhin muss man beachten, dass viele der heutigen HochleistungsMOSFETs intern eine Parallelschaltung vieler kleiner MOSFET-Zellen (z.B. sogenannte Trench-FET) sind, und somit oft für den Linearbetrieb ungeeignet sind. Denn auch dort können einzelne Zellen überhitzen und durchbrennen (Hot Spot). Ob ein MOSFET für den Linearbetrieb tauglich ist steht manchmal im Datenblatt, oft aber eher nicht, eben weil die meisten MOSFETs als Schalter entwickelt und gebaut sind. Typische Vertreter für Linearbetrieb findet man in der [[MOSFET-Übersicht]]. Ein wichtiges Indiz für Linearbetrieb ist eine Kurve für DC im [[#SOA_Diagramm | SOA-Diagramm]]. Meist geht es dort nur bis 10ms, DC fehlt, eben weil DC (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent = Gleichstrom = Linearbetrieb) nicht zulässig ist. Manchmal hat der Hersteller auch &amp;quot;vergessen&amp;quot;, die Kennlinie für DC mit reinzuschreiben, wie z.B. bei [http://www.irf.com/product-info/hi-rel/alerts/fv5-p-09-01-A.pdf IRF], wie in diesem [http://www.mikrocontroller.net/topic/291760#3106758 Beitrag] zu erfahren ist.&lt;br /&gt;
Ein recht gutes Indiz dafür, ob ein FET für den Linearbetrieb taugt, ist die Vorwärtssteilheit. Diese kennzeichnet die Abhängigkeit des Drainstromes von der Ansteuerung am Gate als &amp;lt;math&amp;gt;S = \Delta i_d/\Delta u_{gs}&amp;lt;/math&amp;gt;. Moderne Trench-FET erreichen heute Steilheiten im dreistelligen Bereich und sind für Linearanwendungen völlig unbrauchbar. Zum Vergleich: Der BUZ11 kommt mit gerade einmal 4 bis 5 Siemens daher.&lt;br /&gt;
&lt;br /&gt;
In diesem Beitrag wird die DC-Linie im SOA-Diagramm noch genauer erklärt: [http://www.mikrocontroller.net/topic/319961#3473567 Re: MOSFET Linearbetrieb möglich?]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
* [[Mosfet-Übersicht]]&lt;br /&gt;
* [[IGBT]]&lt;br /&gt;
* [[TRIAC]]&lt;br /&gt;
* [[Kühlkörper]] &lt;br /&gt;
* [[Zwischenkreiskapazität]]&lt;br /&gt;
* [[Treiber]]&lt;br /&gt;
* [[Snippets#Wie_schlie.C3.9Fe_ich_einen_MOSFET_an_einen_Mikrocontroller_an.3F|Wie schließe ich einen Mosfet an einen Mikrocontroller an?]]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/H-Br%C3%BCcken_%C3%9Cbersicht Übersicht H-Brücken]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/168218#1609684 Forumsbeitrag]: MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/186785#new Forumsbeitrag]: nochmal MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/319961#3473567 Forumsbeitrag]: sehr ausführlicher Forumsbeitrag über MOSFETs im Linearbetrieb. Berücksichtigt auch den Spirito-Effekt.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/143324#new Forumsbeitrag]: Über eine elektronische Last, sehr lang&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag]: Logic Level MOSFETs direkt mit einem [[AVR]] treiben.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787855 Forumsbeitrag]: MOSFETs im Linearbetrieb, Laborerfahrungen&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787945 Forumsbeitrag]: MOSFETs für Linearbetrieb&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/269642?goto=2820617#2820617 Forumsbeitrag]: Verpol- und Überspannungsschutz mit MOSFETs&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/425414#4981611 Forumsbeitrag]: Linearbetrieb von MOSFETs&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.elektronikinfo.de/strom/feldeffekttransistoren.htm Feldeffekttransistoren bei elektronikinfo.de]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0207011.htm FET im ELKO]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0510161.htm MOSFET im ELKO]&lt;br /&gt;
* [http://www.sprut.de/electronic/switch/nkanal/nkanal.html MOSFET bei sprut.de]&lt;br /&gt;
* [http://sound.westhost.com/articles/hexfet.htm#51 MOSFETs in Audioendstufen, engl.]&lt;br /&gt;
* [http://irf.custhelp.com/cgi-bin/irf.cfg/php/enduser/std_adp.php?p_faqid=214&amp;amp;p_created=1019728945&amp;amp;p_sid=pt9ITiCj&amp;amp;p_accessibility=0&amp;amp;p_redirect=&amp;amp;p_lva=&amp;amp;p_sp=cF9zcmNoPTEmcF9zb3J0X2J5PSZwX2dyaWRzb3J0PSZwX3Jvd19jbnQ9MTQsMTQmcF9wcm9kcz0mcF9jYXRzPSZwX3B2PSZwX2N2PSZwX3BhZ2U9MSZwX3NlYXJjaF90ZXh0PWxpbmVhcg**&amp;amp;p_li=&amp;amp;p_topview=1 FAQ Answer ID 214 bei IRF zum Linearbetrieb]&lt;br /&gt;
* [http://www.nxp.com/documents/application_note/AN11158.pdf AN11158 - Understanding power MOSFET data sheet parameters] von NXP (PDF)&lt;br /&gt;
* [https://assets.nexperia.com/documents/technical-note/TN00008.pdf TN00008 - Power MOSFET frequently asked questions and answers] von nexperia (PDF)&lt;br /&gt;
* [http://www.infineon.com/dgdl/Infineon+-+Application+Note+-+PowerMOSFETs+-+OptiMOS%E2%84%A2+-+Linear+Mode+Operation+and+SOA+Power+MOSFETs.pdf?fileId=db3a30433e30e4bf013e3646e9381200 AN: Linear Mode Operation andSafe Operating Diagram of Power-MOSFETs] von Infineon (PDF)&lt;br /&gt;
* [http://www.ixys.com/Documents/Articles/Article_Linear_Power_MOSFETs.pdf MOSFETs Withstand Stress of Linear-Mode Operation] Neuentwickelte MOSFETs für Linearbetrieb (PDF)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]] [[Kategorie:Leistungselektronik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Forth&amp;diff=107022</id>
		<title>Forth</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Forth&amp;diff=107022"/>
		<updated>2024-07-16T16:15:27Z</updated>

		<summary type="html">&lt;p&gt;Heha: Vor- und Nachteile&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Forth&#039;&#039;&#039; ist eine stack-orientierte Programmiersprache, die von Chuck Moore Ende der 1960er zur Steuerung von astronomischen Radio-Teleskopen entwickelt und in den 1970ern kommerzialisiert wurde. Forth-Systeme lassen sich für praktisch jeden Mikrocontroller mit einer Mindestmenge an RAM erstellen und es gibt Implementierungen in Hardware.&lt;br /&gt;
&lt;br /&gt;
Forth-Programme bestehen üblicherweise aus vielen kurzen Unterprogrammen (Forth-Worte), was gut geschriebene Programme sehr übersichtlich macht. Weil Parameter über einen separaten Daten-Stack übergeben werden, beeinträchtigt diese Programmierweise die Ausführungsgeschwindigkeit nicht wesentlich.&lt;br /&gt;
&lt;br /&gt;
Forth-Derivate findet man an vielen Stellen, von HP-Taschenrechnern bis zu OpenFirmware. Chip-Implementierungen (die also Forth als Maschinensprache verstehen und etwa beide Stacks in Hardware implementieren) gelten als sehr effektiv, sowohl im Verhältnis von erzielbarer Rechenleistung und Anzahl der Transistoren, als auch bei Energieverbrauch oder bei Context-Wechsel.&lt;br /&gt;
&lt;br /&gt;
Der Grundumfang von Forth, samt Interpreter/Compiler haben benötigt in der Regel nur wenige kByte Code. Da die Sprache ihre eigene Erweiterung unterstützt werden dennoch auch umfangreiche Projekte darin realisiert. Es heißt, dass erfahrene Forth-Programmierer innerhalb eines Projektes aus Forth eine auf das Problem zugeschnittene Sprache schaffen, so dass Sprache und Anwendung nicht mehr direkt unterscheidbar sind. Dies hat in der Vergangenheit oft zu Akzeptanzproblemen geführt.&lt;br /&gt;
&lt;br /&gt;
Es wird oft vermutet, dass einer weiteren Verbreitung von Forth vor allem die späte Standardisierung und die Hardwareabhängigkeit des Codes im Wege standen. Dies mag bei der Verwendung von Forth als allgemeine Programmiersprache für Anwendungen zutreffen. Bei Embedded-Software spielt die Portierbarkeit von Code gegenüber dem Einsatz geeigneter Abstraktionen jedoch eine eher untergeordnete Rolle.&lt;br /&gt;
&lt;br /&gt;
Bei modernen Forth-Systemen für Mikrocontroller ist die Einstiegshürde eher niedriger als etwa bei &amp;quot;C&amp;quot;, da Programme stufenweise aufgebaut und interaktiv getestet werden können.  &lt;br /&gt;
&lt;br /&gt;
Die besonderen Reize von Forth für die Programmierung von Mikrocontrollern liegen:&lt;br /&gt;
&lt;br /&gt;
# in der Kompaktheit des Codes (die Wiederverwendung von kleinsten Codesegmenten kann man in anderen Hochsprachen aus Gründen der Übersichtlichkeit und der Performance nicht realisieren)&lt;br /&gt;
# in der Bandbreite von hardware-nahen Operationen bis zu komplexen Aufgaben&lt;br /&gt;
# in der Interaktivität: über eine serielle Verbindung mit dem Controller kann man direkt den Interpreter auf dem µC nutzen, um direkte Hardwarezugriffe zu steuern oder Programmteile zu testen; es entfällt der Umweg über Debugcode-&amp;gt;Compilieren-&amp;gt;Flashen-&amp;gt;Testen&lt;br /&gt;
&lt;br /&gt;
Den Vorteil der Interaktivität ist bei viele µC-Implementierungen von Forth eingeschränkt, da sie die Compilierung in den Entwickler-PC verlagern; dabei sinkt zwar die Codegröße (Befehlsnamen etc. müssen nicht mehr im Flash stehen), aber es lässt sich nicht mehr interaktiv debuggen.&lt;br /&gt;
&lt;br /&gt;
Forth unterscheidet prinzipiell nicht zwischen Programm und Daten, was bei Implementierungen auf Prozessoren mit [[Harvard-Architektur]] wie AVR zunächst hinderlich ist. Durch die Verwendung eines &amp;quot;innerer Interpreters&amp;quot; lassen sich aber auch diese Systeme mit Forth betreiben.  &lt;br /&gt;
&lt;br /&gt;
== Vorteile ==&lt;br /&gt;
* Sehr kleiner Interpreter/Compiler, der sogar auf AVR laufen kann. Beste Lösung für einen (Kommadozeilen-)Interpreter/Compiler auf der Mikrocontroller-Zielplattform.&lt;br /&gt;
* Forth-Denkschema „verbietet“ Gleitkommazahlen und baut auf Ganzzahlen, ggf. mit Festkomma. Assemblernaher Ansatz, ideal für Controller ohne eingebaute Gleitkomma-Hardware&lt;br /&gt;
* Umformulierung von Infix-Notation in UPN zur Berechnung ist gut zum Verstehen des Compilerbaus — und hält den Forth-Interpreter/Compiler klein&lt;br /&gt;
&lt;br /&gt;
== Nachteile ==&lt;br /&gt;
* Gewöhnungsbedürftige Syntax&lt;br /&gt;
* Fixierung des Ur-Forth auf Systeme mit externem Massenspeicher in Blöcken — ein heutzutage ausgestorbenes Denkschema, Dateien sind, wenn überhaupt vorhanden, &#039;&#039;Array of Byte&#039;&#039;&lt;br /&gt;
* Fixierung des Ur-Forth auf 16-Bit-Prozessoren — sind heutzutage im Mikrocontrollerbereich nahezu ausgestorben, und es dominieren 8 und 32 Bit.&lt;br /&gt;
* Fixierung des Ur-Forth auf Von-Neumann-Systeme mit durchgehend RAM — Meistens haben Mikrocontroller viel zu wenig RAM, dafür ausreichend ROM (Flash).&lt;br /&gt;
* Den in (Ur-)Forth eingebauten Texteditor will keiner ernsthaft noch verwenden — Toter Code.&lt;br /&gt;
* Für Zeichenkettenverarbeitung ist Forth nicht gerade gemacht.&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* Weitere Informationen zur Sprache Forth findet man auf [http://de.wikipedia.org/wiki/Forth_(Programmiersprache) Wikipedia]&lt;br /&gt;
* [http://www.forth-ev.de/ Forth-Gesellschaft e.V.] &lt;br /&gt;
&lt;br /&gt;
* ARM&lt;br /&gt;
** [http://pygmy.utoh.org/riscy/ Riscy Pygness] - Pygmy Forth for the [[ARM]]&lt;br /&gt;
** [http://www.forth-ev.de/article.php/20080312083818313 hForth-IAR Port of hForth] für AT91SAM7&lt;br /&gt;
** [http://mecrisp.sourceforge.net/ Mecrisp-Stellaris]&lt;br /&gt;
** [http://www.forth-ev.de/filemgmt/singlefile.php?lid=602 ARM Sonderheft] des Forth-Magazins &#039;&#039;Vierte Dimension&#039;&#039; (44 Seiten)&lt;br /&gt;
&lt;br /&gt;
* AVR&lt;br /&gt;
** [http://claymore.engineer.gvsu.edu/%7Esteriana/Software/pfavr/index.html PFAVR] - eine ANS Forth Anpassung für [[AVR]] (req.: min. 13K words FLASH and 32Kbytes external RAM)&lt;br /&gt;
** [http://krue.net/avrforth/ avrforth] von Daniel Kruszyna&lt;br /&gt;
** [http://amforth.sourceforge.net amforth] Forth für [[AVR]] Atmega µC (req: nur der Mikrocontroller)&lt;br /&gt;
** [http://www.mikrocontroller.net/topic/94193 Forth-Computer mit ATMega 32 und Videoausgabe] -  Forumsbeitrag von Christian Berger&lt;br /&gt;
** [http://www.forth-ev.de/filemgmt/singlefile.php?lid=134 Vierte Dimension Sonderheft AVR], 2007&lt;br /&gt;
** [http://www.forth-ev.de/article.php/20110423154127859  Neues von amForth (Erich Waelde)]. Video und Folien von der Forth Tagung 2011 in Goslar&lt;br /&gt;
** [https://gitlab.com/jjonethal/eforth328 eForth328], eForth für ATMega328 von Dr.C.H. Ting v2.21 mit Dokumentation, 2011 &lt;br /&gt;
&lt;br /&gt;
* AVR und PIC:&lt;br /&gt;
** [http://flashforth.com/ FlashForth] - ein Forth für PIC18, PIC24/30/33, AVR Atmega&lt;br /&gt;
&lt;br /&gt;
* Arduni Yún &amp;amp; Raspberry Pi&lt;br /&gt;
** [http://thebeez.home.xs4all.nl/4tH/ 4th] - ein Forth-Compiler für viele Systeme&lt;br /&gt;
&lt;br /&gt;
* MSP430&lt;br /&gt;
** [http://www.camelforth.com/page.php?8 CamelForth/430 ], ein ANS Forth für TI [[MSP430]]&lt;br /&gt;
** [http://mecrisp.sourceforge.net/ Mecrisp MSP430 Forth]&lt;br /&gt;
** [https://gitlab.com/Jean-Michel/FastForthForMSP430fr5xxx Fast Forth] für MSP430FR5xxx mit SD CARD LOAD, READ and WRITE&lt;br /&gt;
&lt;br /&gt;
* R8C&lt;br /&gt;
** Anpassung von [http://www.jwdt.com/~paysan/gforth.html Gforth] EC für [[R8C]]&lt;br /&gt;
&lt;br /&gt;
* STM8&lt;br /&gt;
** [http://www.forth.org/svfig/kk/07-2010.html STM8EF eForth für STM8S Discovery] Demo C.H.Ting SVFIG Meeting, July 2010&lt;br /&gt;
** [https://github.com/TG9541/stm8ef STM8 eForth für die STM8 Familie] mit Flash, Multitasking und Erweiterungen, s. [https://wiki.forth-ev.de/lib/exe/fetch.php/events:stm8_eforth._ein_semi-tethered_forth.pdf Vortrag Forth-eV Tagung 2018]&lt;br /&gt;
&lt;br /&gt;
* 1802, 6809, 8051, 8052/C8051F, 8086, Z80&lt;br /&gt;
** [http://www.camelforth.com CamelForth] &lt;br /&gt;
&lt;br /&gt;
*  ARM, RISC-V, Motorola 68000, PDP-11 und asm.js&lt;br /&gt;
** [https://github.com/larsbrinkhoff/lbForth lbForth] Compiler, Interpreter und Meta-Compiler der Cross-Compilierung auch für 6502, 8051, AVR, Cortex-M, MSP430, PDP-8, PIC und STM8 unterstützt&lt;br /&gt;
&lt;br /&gt;
[[Category:Programmiersprachen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Transistor&amp;diff=107012</id>
		<title>Transistor</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Transistor&amp;diff=107012"/>
		<updated>2024-07-11T10:43:33Z</updated>

		<summary type="html">&lt;p&gt;Heha: Triac oder Thyristor ?&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kunstwort aus &amp;quot;transfer resistor&amp;quot;, was etwa so viel bedeutet wie &amp;quot;übertragener [[Widerstand]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In den 1950-ern als praktische Anwendung des [[Halbleiter]]-Effekts erfundenes &amp;quot;solid state&amp;quot; Schalt- und Verstärkerelement, welches sehr klein ist, ohne bewegte Teile auskommt (anders als ein klassisches Relais) und keine energiefressende Heizung benötigt (anders als eine Röhre).&lt;br /&gt;
&lt;br /&gt;
Vom &amp;quot;bipolaren Transistor&amp;quot; (PNP, NPN) weiterentwickelt zum &amp;quot;Feldeffekt-Transistor&amp;quot; ([[FET]]), der heute - gefertigt mit einem preiswerten Verfahren unter Verwendung von Metall-Oxid-Schichten (MOS) - ein wesentliches Element integrierter Schaltkreise (ICs, integrated circuits) darstellt, und damit natürlich auch von [[Mikrocontroller]]n, um die es in diesem Wiki hauptsächlich geht (bzw. gehen sollte).&lt;br /&gt;
&lt;br /&gt;
== Schaltzeichen ==&lt;br /&gt;
[https://electronicsclub.info/images/transbce.gif]&lt;br /&gt;
&lt;br /&gt;
* E: Emitter&lt;br /&gt;
* B: Basis&lt;br /&gt;
* C: Collector    &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In ASCII Schaltplänen sehen Transistoren so aus:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
                       |&amp;gt;                 |/&lt;br /&gt;
NPN      |&amp;gt;   oder    -|       oder      -|&lt;br /&gt;
                       |\                 |&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                       |&amp;lt;                 |/&lt;br /&gt;
PNP:     |&amp;lt;   oder    -|       oder      -|&lt;br /&gt;
                       |\                 |&amp;lt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um zu erkennen, ob ein NPN oder PNP Transistor im Schaltplan verwendet wird, gibt es Eselsbrücken:&lt;br /&gt;
*Für Dichter: &#039;&#039;&#039;Tut der Pfeil der Basis weh, handelt&#039;s sich um PNP.&#039;&#039;&#039;&lt;br /&gt;
*Für Praktiker: &#039;&#039;&#039;PNP heisst &amp;quot;Pfeil Nach Platte&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
*Mit Dialekt: &#039;&#039;&#039;NPN &amp;quot;&#039;naus, Pfeil, &#039;naus&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
*..und für Gleichberechtigungsverfechter: &#039;&#039;&#039;NPN means &amp;quot;Not Pointing iN&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
JFET: [[Bild:Transistor_JFET.png]]&lt;br /&gt;
&lt;br /&gt;
MOSFET: [[Bild:Transistor_MOSFET.png]]&lt;br /&gt;
&lt;br /&gt;
* S: Source&lt;br /&gt;
* G: Gate&lt;br /&gt;
* D: Drain&lt;br /&gt;
&lt;br /&gt;
Eigentlich haben MOSFETs noch einen vierten Anschluss namens Bulk. Der ist aber nur bei Spezialtypen als Pin herausgeführt. Im Normalfall kann man ihn vergessen, da er nicht gesondert beschaltet werden muss, er ist praktisch und auch im Symbol mit Source verbunden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Die NPN/PNP Eselsbrücken funktionieren bei FETs nicht, denn bei einem P-Kanal FET zeigt der Pfeil weg vom FET!&lt;br /&gt;
Ein Bipolartransistor, im Englischen als bipolar junction transistor (BJT) bezeichnet, ist ein Transistor, bei dem Ladungsträger – negativ geladene Elektronen und positiv geladene Defektelektronen – zum Stromtransport durch den Bipolartransistor beitragen. Der BJT wird mittels eines elektrischen Stroms gesteuert und wird zum Schalten und Verstärken von Signalen ohne mechanisch bewegte Teile eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Bipolare Leistungstransistoren sind für das Schalten und Verstärken von Signalen höherer Stromstärken und Spannungen ausgelegt.&lt;br /&gt;
&lt;br /&gt;
== Typbezeichnungen == &lt;br /&gt;
&lt;br /&gt;
Neben den Typbezeichnungen wie 2Nxxxx, TIPxxx, MJxxx, MJExx gibt es noch die in Europa geläufigere  Kennzeichnung bestehend aus zwei Buchstaben und drei Ziffern. Die diversen Kennzeichnungsmöglichkeiten sind in einem eigenen Artikel ([[Kennzeichnung von Halbleitern]]) zusammengefasst.&lt;br /&gt;
&lt;br /&gt;
== Kenndaten/Parameter ==&lt;br /&gt;
&lt;br /&gt;
Im [http://www.mikrocontroller.net/topic/197676#1938546 Beitrag: Transistorparameter Erklärung] sind Links zu Erläuterungen spezieller Kürzel.&lt;br /&gt;
&lt;br /&gt;
== Transistor Grundschaltungen ==&lt;br /&gt;
&lt;br /&gt;
Generell gilt, dass Strom vom Kollektor zum Emitter nur dann fließen kann, wenn die Basis positiver (NPN) bzw. negativer (PNP) wird als der Emitter. Dabei darf die Basis nicht direkt mit Vcc (NPN) oder GND (PNP) verbunden werden, da der Basisstrom sonst zu gross wird. Es muss jeweils ein geeigneter Basiswiderstand (R_Basis) gewählt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* Artikel [[Basiswiderstand]]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0203111.htm Transistor Grundschaltungen im ElKo]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Transistor Transistor bei RoboterNetz.de]&lt;br /&gt;
&lt;br /&gt;
Es gibt drei Grundschaltungen. Der Name beschreibt den Anschluss, welcher sich auf einem festen Potential (Spannung) befindet. Die beiden anderen Anschlüsse haben bedingt durch die Schaltung ein veränderliches Potential.&lt;br /&gt;
&lt;br /&gt;
=== Kollektorschaltung (Emitterfolger)===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anwendung:&#039;&#039;&#039;&lt;br /&gt;
* Impedanzwandler&lt;br /&gt;
* Darlington-Schaltung&lt;br /&gt;
* Schalter&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Keine Phasendrehung&lt;br /&gt;
* Hohe Stromverstärkung&lt;br /&gt;
* Keine Spannungsverstärkung&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel: Transistor als Schalter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* NPN: Kollektor mit Vcc verbinden, Last an Emitter&lt;br /&gt;
* PNP: Kollektor mit GND verbinden, Last an Emitter&lt;br /&gt;
&lt;br /&gt;
In diesem Fall regelt der Transistor die Spannungen am Emitter, daher wird die Last am Emitter angeschlossen. Die Spannung am Emitter entspricht immer der an der Basis minus 0,6V, sie folgt der Basisspannung, deswegen auch der Name Emitterfolger. Daher ist diese Schaltung nicht geeignet, um 12V mit 5V zu schalten. &lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[Bild:NPN_Collector.gif | framed | center | NPN Transistor in Kollektorschaltung]] &lt;br /&gt;
|| [[Bild:PNP_Collector.gif | framed | center | PNP Transistor in Kollektorschaltung]]  &lt;br /&gt;
|}&lt;br /&gt;
* NPN: Wird &amp;lt;math&amp;gt;R_{Poti}&amp;lt;/math&amp;gt; (Spannungsteiler) erhöht, &#039;&#039;&#039;steigt&#039;&#039;&#039; die Spannung &amp;lt;math&amp;gt;U_{Last}&amp;lt;/math&amp;gt; letztlich bis auf VCC-0,6V (Basis-Emitter-Übergang).&lt;br /&gt;
* PNP: Wird &amp;lt;math&amp;gt;R_{Poti}&amp;lt;/math&amp;gt; (Spannungsteiler) erhöht, &#039;&#039;&#039;sinkt&#039;&#039;&#039; Spannung an &amp;lt;math&amp;gt;U_{Last}&amp;lt;/math&amp;gt; letztlich bis auf 0,6V (Basis-Emitter-Übergang).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0204133.htm Kollektorschaltung im ElKo]&lt;br /&gt;
&lt;br /&gt;
=== Emitterschaltung ===&lt;br /&gt;
&lt;br /&gt;
Die Emitterschaltung bietet hohe Spannungs- und Stromverstärkung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anwendung:&#039;&#039;&#039;&lt;br /&gt;
* NF- und HF-Verstärker&lt;br /&gt;
* Leistungsverstärker&lt;br /&gt;
* Transistor als Schalter&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Phasendrehung 180°&lt;br /&gt;
* Hohe Spannungsverstärkung&lt;br /&gt;
* Hohe Stromverstärkung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel: Transistor als Schalter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Last liegt am Kollektor. Der Strom durch den Schalter oder an U_Schalt steuert den Strom zwischen Kollektor und Emitter. Wird der Schalter geschlossen, fließt ein Strom.&lt;br /&gt;
&lt;br /&gt;
[[Bild:NPN_Schalter.gif | framed | center | NPN Transistor als Schalter]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                   ___                                          ___&lt;br /&gt;
Vcc/+ o-----------------------+          Vcc/+ o-----------------------+&lt;br /&gt;
                              |                                        |&lt;br /&gt;
                     ___    |&amp;lt;            U_schalt (-)       ___     |&amp;lt;  PNP             &lt;br /&gt;
             +------|___|---|  PNP             o------------|___|----|&lt;br /&gt;
             |     R_Basis  |\                             R_Basis   |\&lt;br /&gt;
    Schalter \                |                                        |&lt;br /&gt;
             |       ___      |                               ___      |&lt;br /&gt;
GND/- o------+------|___|-----+          GND/- o-------------|___|-----+&lt;br /&gt;
                   R_Last                                    R_Last     &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0204302.htm Emitterschaltung im ElKo]&lt;br /&gt;
&lt;br /&gt;
=== Basisschaltung ===&lt;br /&gt;
&lt;br /&gt;
Die Basisschaltung findet sich vor allem in Eingangsstufen in der HF-Technik. Im Schaltbetrieb wird sie praktisch nur zur [[Pegelwandler#STEP-UP: 5V -&amp;gt; 9..15V | Pegelwandlung]] für nachfolgende Stufen verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Geringe Eingangsimpedanz&lt;br /&gt;
* Keine Phasenverschiebung&lt;br /&gt;
* Hohe Bandbreite&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0205081.htm Basisschaltung im ElKo&lt;br /&gt;
&lt;br /&gt;
== FAQ aus dem Forum ==&lt;br /&gt;
&lt;br /&gt;
=== PNP/NPN als Schalter, wohin mit der Last? === &lt;br /&gt;
Für viele einfache Anwendungen kann man sich merken: &#039;&#039;&#039;Bei Schaltanwendungen darf der Basisstrom nicht durch die Last fließen&#039;&#039;&#039; (… wenn die zu schaltende Spannung höher ist als die Steuerspannung). Normalerweise kommt dabei die Emitterschaltung zum Einsatz, die Last kommt also an den Kollektor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 Ucc o─────────────┐                  Ucc o───────────┐&lt;br /&gt;
                   │                                  │&lt;br /&gt;
                  ┏┷┓               An: GND   ___   ┃&amp;lt;&lt;br /&gt;
                  ┃ ┃ R_Last             o───┤___├──┨   PNP&lt;br /&gt;
                  ┗┯┛               Aus: Ucc        ┃\&lt;br /&gt;
                   │                                  │&lt;br /&gt;
An: Ucc  ___     ┃/                                  ┏┷┓&lt;br /&gt;
    o───┤___├────┨   NPN                             ┃ ┃ R_Last&lt;br /&gt;
Aus: GND         ┃&amp;gt;                                  ┗┯┛&lt;br /&gt;
                   │                                  │&lt;br /&gt;
 GND o─────────────┘                  GND o───────────┘&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe [[Basiswiderstand]] zur Berechnung des notwendigen Basiswiderstandes bei gegebener Last R_Last für einen Transistor als Schalter.&lt;br /&gt;
&lt;br /&gt;
Siehe auch Threads im Forum: &lt;br /&gt;
* http://www.mikrocontroller.net/topic/58567 &lt;br /&gt;
* http://www.mikrocontroller.net/topic/119841&lt;br /&gt;
* oder im [http://www.elektronik-kompendium.de/sites/slt/0208031.htm Elektronik-Kompendium-Forum]&lt;br /&gt;
&lt;br /&gt;
Ist die zu schaltende Spannung &#039;&#039;&#039;genauso hoch&#039;&#039;&#039; wie die Steuerspannung darf die Last an den Emitter, und der Basiswiderstand entfällt. Der Transistor arbeitet als &#039;&#039;Impedanzwandler&#039;&#039; und steigert die Treiberfähigkeit des Mikrocontroller-Ausgangs in den Ampere-Bereich. Dieser Trick funktioniert &#039;&#039;&#039;nur&#039;&#039;&#039; mit Bipolartransistoren.&lt;br /&gt;
&lt;br /&gt;
Ist die effektive Invertierung des Schaltzustandes im oben angegebenen Bild lästig &#039;&#039;&#039;und&#039;&#039;&#039; der Strom geringer als der Ausgangsstrom, kann man den Transistor am Emitter steuern. Eine solche Schaltung funktioniert noch besser mit (Logik-Level-)MOSFETs und stellt eine einfache Kopplung zwischen Systemen mit unterschiedlicher Betriebsspannung (bspw. 1,8 V und 3,3 V) her. MOSFETs werden dazu am Source-Pin gesteuert, während das Gate an der „gegenüberliegenden“ Speisespannung „fixiert“ wird.&lt;br /&gt;
&lt;br /&gt;
=== Wie kann ich mit 5V vom Mikrocontroller 12V und mehr schalten? === &lt;br /&gt;
Schau mal hier:&lt;br /&gt;
* Wikiartikel [[Pegelwandler]]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/praxis/bausatz_pegelwandler-mit-transistoren.htm Pegelwandler im ElKo]&lt;br /&gt;
* [http://dl6gl.de/grundlagen/schalten-mit-transistoren schalten-mit-transistoren]&lt;br /&gt;
&lt;br /&gt;
oder in diesen Threads:&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/17899 Transistor als Schalter]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/14437 Vcc schalten mit MOSFET]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/29830 Schalten mit PNP-Transistor]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/104027 Transistorschalter für Versorgungsspannung]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/104951#921417 7-50V strombegrenzt schalten]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/103116#900247 P-Kanal MOSFET ansteuern]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/383039#4367479 5V/15mA mit 3,3V schalten]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/401317#4638354 High Side Treiber mit Ladungspumpe]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/347885?goto=3855574#3855574 minus -12V mit 3,3V schalten]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/516373?goto=6650986#6650986 Kurzschlußfester Schalter für VCC]&lt;br /&gt;
&lt;br /&gt;
Bei der Kollektor-Schaltung entspricht die Spannung am Emitter immer der an der Basis, daher ist sie nur bedingt geeignet. Zum Schalten können die folgenden Emitter-Schaltungen verwendet werden. Achtung: In der zweiten davon arbeitet T1 in Basisschaltung.&lt;br /&gt;
&lt;br /&gt;
Schalten gegen GND&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 +12V o------------------------+&lt;br /&gt;
                               |&lt;br /&gt;
                              .-. &lt;br /&gt;
                             ( X )  &lt;br /&gt;
                              &#039;-&#039;&lt;br /&gt;
                               |&lt;br /&gt;
                    ___      |/ T1,NPN   &lt;br /&gt;
        uC PIN o---|___|-----| BC547     &lt;br /&gt;
                   R2,4K7    |&amp;gt;&lt;br /&gt;
                               |&lt;br /&gt;
  GND o---------o--------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Schalten gegen +12V&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 +12V o--------------+----------------------+&lt;br /&gt;
                     |                      |&lt;br /&gt;
                     |   ____              |&amp;lt; T2, PNP&lt;br /&gt;
                     +--|____|----+--------|  BC557&lt;br /&gt;
                        R1,4K7    |        |\&lt;br /&gt;
                                |/T1,NPN    |&lt;br /&gt;
         Vcc/+5V o--------------| BC547     |&lt;br /&gt;
                                |&amp;gt;          |&lt;br /&gt;
                        ___       |        .-. &lt;br /&gt;
          uC PIN o-----|___|------+       ( X )  &lt;br /&gt;
                       R2,4K7              &#039;-&#039;&lt;br /&gt;
                                            |&lt;br /&gt;
  GND o----------o--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Transistor an µC ohne Vorwiderstand === &lt;br /&gt;
&lt;br /&gt;
Normalerweise sind IO Pins vom µC nicht in der Lage, große Ströme zu treiben, beim AVR maximal ~20mA. Für einen kleinen Transistor ist das immer noch zu viel und es wäre auch Stromverschwendung.&lt;br /&gt;
&lt;br /&gt;
Deshalb kann man den IO-Pin des AVRs einfach als Tristate Eingang einstellen (Portpin als Eingang und Pullup deaktivieren), damit kein Basisstrom fließt.&lt;br /&gt;
&lt;br /&gt;
Aktiviert man nun den internen Pullup-Widerstand des AVRs, agiert dieser als Basisvorwiderstand, und es fließt nur ein geringer Basisstrom (die Pullups eines AVRs liegen irgendwo bei 50k bis 100k Ohm - siehe Datenblatt).&lt;br /&gt;
&lt;br /&gt;
Nur sollte man bei kleinen Transistoren aufpassen, dass man den Portpin in der Software nie als aktiven Ausgang schaltet. &lt;br /&gt;
&lt;br /&gt;
Wenn der verwendete µC zuschaltbare Pulldown-Widerstände an seinen Pins besitzt, kann man das gleiche auch mit einem PNP-Transistor machen (natürlich nur den Pulldown aktivieren).&lt;br /&gt;
&lt;br /&gt;
Eine Anwendung wären z.&amp;amp;nbsp;B. Nixie-Röhren-Kathodentreiber (geringe Stromverstärkung nötig).&lt;br /&gt;
&lt;br /&gt;
=== Wann bipolare (NPN/PNP) und wann FETs (insbesonders, wenn LEDs im Spiel sind)?=== &lt;br /&gt;
Oft sind bipolare Transistoren (NPN/PNP) schon ausreichend, vor allem wenn &amp;quot;normale&amp;quot; LEDs (20mA) verwendet werden. FETs sind u.a. dann gut, wenn mit geringen Eingangsströmen hohe Ausgangsströme (über 300 mA) geschaltet werden sollen, also bei den Power-LEDs (Luxeon...).&lt;br /&gt;
&lt;br /&gt;
Ein Grenzfall: 500mA/5V schalten, siehe http://www.mikrocontroller.net/topic/62327.&lt;br /&gt;
&lt;br /&gt;
=== Wieso gehen bei einer Multiplex-Anzeige mit Schieberegister 74HC595 und (Darlington-)Transistor als Zeilentreiber die LED nicht ganz aus?  ===&lt;br /&gt;
Das liegt an der &#039;&#039;&#039;Miller-Kapazität&#039;&#039;&#039; des übersteuerten (Darlington-)Transistors. Der braucht erst mal einige 10µs, um zu sperren. Du mußt also erstmal beide Zeilen ausschalten, dann etwas warten und dann die nächste Zeile an. Oder Du ersetzt die Darlington durch P-FETs. [http://www.mikrocontroller.net/topic/132545]&lt;br /&gt;
&lt;br /&gt;
=== Wie steuert man ein Relais? ===&lt;br /&gt;
Normalerweise verwendet man zur Ansteuerung von Relais NPN-Transistoren in Emitterschaltung. Freilaufdiode nicht vergessen! &lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Relais mit Logik ansteuern]]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/22023/Relaisanteuerung.png Schaltbilder aus dem Forum]&lt;br /&gt;
&lt;br /&gt;
=== Was ist die Spannung &amp;lt;math&amp;gt;U_{BE\_sat}&amp;lt;/math&amp;gt; (lt. Datenblatt max. 1,2V)? ===&lt;br /&gt;
Bekanntlich verhält sich die Basis-Emitter Strecke eines Transistors wie eine Diode und &amp;lt;math&amp;gt;U_{BE\_sat}&amp;lt;/math&amp;gt; ist die bei maximal zulässigem Basisstrom anliegende Vorwärtsspannung.&lt;br /&gt;
&lt;br /&gt;
=== Was bewirkt ein Kondensator (100µF-1nF) parallel zur Basis-Emitter-Strecke nach Masse? === &lt;br /&gt;
Er wirkt mit dem Basisvorwiderstand als RC-Tiefpass. Damit wird der Transistor eigentlich nicht mehr als Schalter, sondern als Linearregler betrieben. Manche Verstärker-Schaltungen sind, gerade bei hohen Lasten, sehr schwingfreudig. Deswegen ist bei PWM so ein C nicht sinnvoll.&lt;br /&gt;
&lt;br /&gt;
=== Gibt es einen IC, der wie mehrere Transistoren funktioniert? ===&lt;br /&gt;
&lt;br /&gt;
Gibt es! Beispielsweise der &#039;&#039;&#039;ULN2803&#039;&#039;&#039; ist ein 8-fach Darlington Transistor Array mit [[Ausgangsstufen_Logik-ICs#Open_Collector|Open-Collector Ausgang]]. Und sogar die Freilaufdioden sind schon drin.&lt;br /&gt;
Damit lässt sich z.&amp;amp;nbsp;B. ein Leistungstreiber zur Ansteuerung von Schrittmotoren, Relais und anderen induktiven Lasten aufbauen.&lt;br /&gt;
&lt;br /&gt;
=== Gibt es ein Transistor-Array wie ULN28xx, das gegen Vcc schaltet? ===&lt;br /&gt;
&lt;br /&gt;
Such mal nach UDN29xx, z.&amp;amp;nbsp;B. UDN2981, UDN2987 ...&lt;br /&gt;
&lt;br /&gt;
Allegro hat zwischenzeitlich seinen Source-Treiber umbenannt. Die Bauteile heißen jetzt A2981, A2982 usw... Die Datenblätter sind identisch geblieben.&lt;br /&gt;
&lt;br /&gt;
Nachteil eines Darlington-Source-Treibers ist die hohe Sättigungsspannung (VceSat) von typisch 1,2 V. Die Eingangsspannung sollte dementsprechend höher ausgelegt werden oder gleich ein Source-Treiber mit MOSFET-Transistoren gewählt werden (z.B. Infineon ProFETs).&lt;br /&gt;
&lt;br /&gt;
=== Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein? ===&lt;br /&gt;
siehe [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
Die Summe allen Übels ist konstant. Man muss wissen, welches Bauteil sich wofür besonders eignet.&lt;br /&gt;
&lt;br /&gt;
====[[FET | MOSFET]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Bei niedrigen Spannungen &amp;lt;100V sehr gut geeignet, &amp;lt;200V gut geeignet, sehr geringe R_DS-ON Widerstände möglich (einstelliger mOhm-Bereich)&lt;br /&gt;
* Hohe Schaltgeschwindigkeiten möglich&lt;br /&gt;
* Geringe An- und Ausschaltverluste&lt;br /&gt;
* Statisch praktisch leistungslos steuerbar&lt;br /&gt;
* Bodydiode kann als Freilaufdiode in H-Brücken verwendet werden&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Drain-Source Sperrspannung zerstört das Bauteil nicht, wenn der Strom sowie die Energie begrenzt werden. (Verhalten wie [[Diode | Z-Diode]])&lt;br /&gt;
   &lt;br /&gt;
Nachteile&lt;br /&gt;
*Antiparallele Diode (Bodydiode) ist in nahezu allen MOSFETs unvermeidlich, daduch Sperren nur in einer Polarität möglich, Stromfluss über den MOSFET aber in beiden Richtungen möglich (Inversbetrieb, Synchrongleichrichter)&lt;br /&gt;
* Bei Sperrspannungen &amp;gt;100V deutlich steigende Einschaltwiderstände (R_DS-ON)&lt;br /&gt;
* Schnelles Umschalten erfordert hohe Lade-und Entladeströme ([[MOSFET-Übersicht#Mosfet-Treiber|MOSFET-Treiber]])&lt;br /&gt;
* Leitverluste quadratisch proportional zum Strom, Pv = I²*R_DS-ON&lt;br /&gt;
&lt;br /&gt;
====[[Transistor | Bipolartransistor]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Hohe Spannungsfestigkeiten möglich, bis zu 2000V&lt;br /&gt;
* Sehr hohe Schaltgeschwindigkeiten möglich&lt;br /&gt;
* Leitverluste etwa linear proportional zum Strom und Kollektor-Emitter-Sättigungsspannung, Pv = I * Uce-sat, U_CEC-sat typ 0,1...2.5 V&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Stromgesteuert, damit wird immer eine gewisse Ansteuerleistung benötigt&lt;br /&gt;
* Bei grossen Kollektorströmen nimmt die Stromverstärkung deutlich ab, dann wird ein großer Basisstrom benötigt&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Kollektor-Emitter Sperrspannung zerstört das Bauteil&lt;br /&gt;
&lt;br /&gt;
====[[IGBT]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Hohe Spannungsfestigkeiten möglich, bis zu 6600V, in üblichen Bauformen bis ca 1700V gut verfügbar&lt;br /&gt;
* Statisch praktisch leistungslos steuerbar&lt;br /&gt;
* Mit oder ohne antiparallele Diode zur Kollektor-Emitter-Strecke verfügbar&lt;br /&gt;
* Leitverluste linear proportional zum Strom und Kollektor-Emitter-Sättigungsspannung, Pv = I * Uce-sat, U_CE-sat typ. 2V&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Nur mäßige Schaltfrequenzen möglich (typ. &amp;lt;30..50kHz)&lt;br /&gt;
* Schnelles Umschalten erfordert hohe Lade-und Entladeströme ([[MOSFET-Übersicht#Mosfet-Treiber|MOSFET-Treiber]])&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Kollektor-Emitter Sperrspannung zerstört das Bauteil&lt;br /&gt;
&lt;br /&gt;
====[[TRIAC|Triac]] / Thyristor====&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Sehr hohe Spannungsfestigkeiten möglich (800V...mehrere kV, Thyristoren bis 12kV)&lt;br /&gt;
* Mit kurzen Pulsen einschaltbar, danach Selbsthaltung des Stromflusses&lt;br /&gt;
* Leitverluste linear proportional zum Strom, Pv = I * Uak, Uak typ. 0,8...1,5 V &lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Auf niedrige Schaltfrequenzen beschränkt (kHz-Bereich, Schaltzeit im Bereich von µs)&lt;br /&gt;
* Anstiegsgeschwindigkeit des Stromes muss begrenzt werden, sonst kommt es zu Bauteilschäden&lt;br /&gt;
* Anstiegsgeschwindigkeit der Spannung muss begrenzt werden, sonst kommt es zum ungewollten Zünden&lt;br /&gt;
* Stromfluss kann nicht ausgeschaltet werden, damit meist nur Einsatz an Wechselspannung&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50em&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Bauteil               &lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | optimales&amp;lt;br&amp;gt;Einsatzgebiet&lt;br /&gt;
! Kommentar&lt;br /&gt;
|-&lt;br /&gt;
| MOSFET                || 0..200V, 0..500A || im Kleinspannungsbereich meist die beste Wahl als Schalter&lt;br /&gt;
|-&lt;br /&gt;
| Bipolartransistor     || 0..1000V, 0..10A || wird mehr und mehr von MOSFETs verdrängt&lt;br /&gt;
|-&lt;br /&gt;
| IGBT                  || 200..1700V, 0..500A || optimal für hohe Spannungen und hohe Ströme&lt;br /&gt;
|-&lt;br /&gt;
| Triac/Thyristor       || 230V, 400V, 680V,&amp;lt;br&amp;gt;bis mehrere kV, 0..100A || meist für Wechselspannung, &amp;lt;br&amp;gt;Thyristoren bis 1000A im Dauerbetrieb, &amp;lt;br&amp;gt;im Pulsbetrieb einige kA. (Scheibenzelle)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Triac oder Thyristor?&lt;br /&gt;
* Im Kleinleistungsbereich (&amp;lt; 1 W) überwiegen Thyristoren, da das Einsatzgebiet &#039;&#039;nicht&#039;&#039; das Steuern von Wechselspannungsverbrauchern sondern etwa Spannungsbegrenzung (Crowbar) ist. Typische Bauform: TO92.&lt;br /&gt;
* Im Haushaltleistungsbereich (&amp;lt; 2000 W) werden ausschließlich Triacs verwendet. Für induktive Lasten (= hoher Spannungsanstieg beim Ausschalten beim Strom-Nulldurchgang) gibt es „snubberless“-Typen mit verringerter Miller-Kapazität. Typisch für alle Triacs ist ein eingebauter Gate-KA-Widerstand von ≈ 100 Ω. Typische Bauform: TO220.&lt;br /&gt;
* Genauso wie netzfrequente Dioden kommen Thyristoren und Triacs mit einem wesentlich (Richtwert: Faktor 10) höheren Strom als ihrem Nennstrom zurecht, &#039;&#039;sofern&#039;&#039; diese Belastung auf &#039;&#039;eine&#039;&#039; Netzhalbwelle begrenzt ist. Danach muss die Strombelastung sinken bis zum Nennwert. Die Strombelastung ist der &amp;lt;u&amp;gt;gleichgerichtete Mittelwert&amp;lt;/u&amp;gt;. Achtung: Die Spannungsbelastbarkeit bezieht sich hingegen auf den &amp;lt;u&amp;gt;Scheitelwert&amp;lt;/u&amp;gt;! Welche Grenzen einzuhalten sind steht im Datenblatt.&lt;br /&gt;
* Bei Strömen &amp;gt; 40 A werden 2 antiparallele Thyristoren günstiger, weil sich der Strom und die Wärmeentwicklung auf 2 Bauelemente verteilt. Antiparallele Thyristoren gibt es im gemeinsamen Gehäuse. Mit Optokoppler integriert nennt man diese Halbleiterrelais, Festkörperrelais oder &#039;&#039;solid state relay&#039;&#039;. Keine typische Bauform.&lt;br /&gt;
* Für höhere Spannungen als ≈ 1 kV gibt es nur noch Thyristoren. Dabei ist deren Ansteuerung mit Zündtransformator üblich.&lt;br /&gt;
&lt;br /&gt;
=== Wie finde ich den richtigen Transistor für eine LED-Ansteuerung? ===&lt;br /&gt;
&lt;br /&gt;
Quelle: Beiträge [http://www.mikrocontroller.net/topic/157763#1493623] und [http://www.mikrocontroller.net/topic/157763#1494972] von yalu&lt;br /&gt;
&lt;br /&gt;
Um am Anfang wenigstens ein bisschen den Durchblick im Transistordschungel zu behalten, kannst du folgendermaßen vorgehen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nomenklatur&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nach der amerikanischen Nomenklatur beginnen die Transistornamen meist mit 2N (z.&amp;amp;nbsp;B. 2N2222 oder 2N3055) und nach der japanischen mit 2S (z.&amp;amp;nbsp;B. 2SC1815). Für den Anfang kann man sich auf europäische Transistoren beschränken, da es diese in ausreichender Auswahl gibt und die Bezeichnungen relativ gut den Transistortyp wiedergeben:&lt;br /&gt;
&lt;br /&gt;
Der erste Buchstabe bezeichnet das Halbleitermaterial (A=Germanium, B=Silizium). Germaniumtransistoren werden heute nur noch selten verwendet.&lt;br /&gt;
&lt;br /&gt;
Der zweite Buchstabe steht für den Einsatzzweck (C=Universal, D=hohe Leistung, F=Hochfrequenz, U=hohe Spannung).&lt;br /&gt;
&lt;br /&gt;
So ist also ein ACxxx ein Germaniumuniversaltransistor und ein BDxxx ein Siliziumleistungstransistor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auswahl&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn du dich für einen Grundtyp entschieden hast (für die LED ist ein BC-Typ das Richtige), gehst du auf die Webseite eines Elektronikhändlers (Reichelt, Kessler usw.), schlägst die Seite mit den BC-Transistoren auf. Da gibt es natürlich sehr viele  davon, und du brauchst jetzt eine&lt;br /&gt;
Suchreihenfolge. Als erstes Auswahlkriterium nimmst du den Preis, denn:&lt;br /&gt;
&lt;br /&gt;
* Zuviel Geld hast wahrscheinlich nicht einmal du.&lt;br /&gt;
* Billig ist meist das, was in großen Stückzahlen hergestellt wird. Was für die Masse gut ist, ist (zumindest in diesem Fall) meist auch für dich gut.&lt;br /&gt;
* Was billig und damit in Massen verkauft wird, bekommst du auch bei anderen Händlern und auch noch in 10 Jahren. Das ist wichtig, wenn deine Schaltung irgendwann einmal in Serie gefertigt werden soll.&lt;br /&gt;
&lt;br /&gt;
Gleich als nächstes überlegst du, ob du einen NPN- oder einen PNP-Typ brauchst. Das ergibt sich aus der Anordnung der Bauteile in deiner Schaltung. Hast du die Möglichkeit, die Schaltung wahlweise für einen NPN- oder einen PNP-Typ auszulegen, wählst du die Variante mit dem NPN-Typ. Um einfach eine LED über einen Mikrocontroller einzuschalten, ist i.Allg. ein NPN-Typ in Emitterschaltung richtig.&lt;br /&gt;
&lt;br /&gt;
Ein weiteres wichtiges Kriterium ist die Befestigungstechnik: Wenn dir die SMD-Löterei etwas suspekt ist, lässt du die entsprechenden Modelle erst einmal alle außen vor. Ein typisches Nicht-SMD-Gehäuse für Universaltransistoren ist TO-92. Es gibt im Internet bebilderte Listen mit den einzelnen Gehäuseformen ([[IC-Gehäuseformen#Weblinks]])&lt;br /&gt;
&lt;br /&gt;
Wenn du jetzt also bei Reichelt  die BC-Transistoren nach Preis aufsteigend sortiert hast, siehst du erst einen Schwung SMD-Tranistoren. Dann kommen ein paar Transistoren im TO-92-Gehäuse, die sind aber PNP. Etwas weiter unten kommt der erste NPN-Transistor in TO-92, nämlich der BC547C. Netterweise stehen gleich ein paar Eckdaten dabei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
BC547C  45V  0,1A  0,5W&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die 45V sind die maximale Kollektor-Emitter-Spannung, in deinem Fall also die Spannung, die du maximal schalten kannst. Da die LED bei Weitem keine 45V braucht und deine Versorgungsspannung eher in der Gegend von 5V liegt, bist du auf jeden Fall auf der sicheren Seite.&lt;br /&gt;
&lt;br /&gt;
Deine LED wird typisch mit 20mA (max. 30mA) betrieben. Der BC547C kann 100mA, also ist auch hier noch Luft.&lt;br /&gt;
&lt;br /&gt;
Zur maximalen Verlustleistung (0,5W): Wenn deine LED eingeschaltet ist, fließen bspw. 20mA. Ist der Transistor voll durchgesteuert (in Sättigung) beträgt die Kollektor-Emitter-Spannung bei diesem geringen Strom typischerweise zwischen 0,1V und 0,2V (Genaueres steht im Datenblatt). Am Transistor wird also maximal die Leistung 20mA·0,2V=4mW in Wärme umgesetzt. Bis zu 500mW dürfen es sein, also ebenfalls ok.&lt;br /&gt;
&lt;br /&gt;
Nachdem du den Transistor in engere Auswahl gezogen hast, lohnt sich auf jeden Fall ein Blick ins Datenblatt. Aus den Tabellen und Diagrammen erfährst du bspw., wie hoch der Basisstrom sein muss, um den Kollektorstrom von mindestens 20mA bei ausreichend geringer CE-Spannung bereitzustellen. Dort ist auch erklärt warum es einen BC547A, BC547B und BC547C gibt. Der letzte Buchstabe gibt nämlich die Stromverstärkungsklasse an. Da eine hohe Stromverstärkung meist wünschenswert ist und in diesem Fall keinen Aufpreis kostet, ziehst du den BC547C den anderen beiden vor.&lt;br /&gt;
&lt;br /&gt;
Da in deiner Anwendung HF- und Rauschverhalten keine Rolle spielen, bist du schon am Ziel angelangt.&lt;br /&gt;
&lt;br /&gt;
Würde deine LED 100mA statt 20mA benötigen, wären die max. 100mA des BC547 etwas knapp bemessen. Du blätterst also in der Reichelt-Liste weiter und stößt auf den BC337-40 mit 45V, 0,5A und 0,525W. Das ist genau das, wonach du suchst. Bei diesem Transistor sind die Stromverstärkungsklassen durch die Endungen -16, -25 und -40 gekennzeichnet. Es wäre ja auch&lt;br /&gt;
zu einfach, wenn immer nur A, B und C verwendet würde ;-)&lt;br /&gt;
&lt;br /&gt;
Bei Strömen ab etwa 500mA kommt man an die Grenze der Leistungsfähigkeit der BC-Typen. Dann geht es weiter mit BD. Der BD135 geht bspw. schon bis 1,5A. Das Problem bei solchen größeren Transistoren: Die Stromverstärkung ist nicht besonders hoch, so dass irgendwann der Mikrocontroller nicht mehr den benötigten Basisstrom liefern kann. Dann muss dem großen Transistor ein kleiner vorangeschaltet werden, um den erhöhten Basisstrom bereitszustellen. Man kann diese Kombination von zwei Transistoren auch fertig als Darlington-Transistor kaufen, von denen ebenfalls einige in der BD-Reihe zu finden sind (z.&amp;amp;nbsp;B. BD647). Ein Transistortyp, der sich sehr gut zum Schalten höherer Ströme eignet, ist der [[FET | MOSFET]].&lt;br /&gt;
&lt;br /&gt;
Wie schon oben angedeutet: Wenn die 30-80V die die meisten BC- und BD-Transistoren abkönnen, nicht ausreichen, suchst du weiter bei BU.&lt;br /&gt;
&lt;br /&gt;
Steigst du in die HF-Technik ein, sind BF-Transistoren eher das Richtige, wobei bei HF-Anwendungen die Auswahl der Transistoren nicht mehr das Schwierigste ist ;-)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Und wie geht&#039;s weiter?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Man könnte natürlich noch viel mehr zu diesem Thema schreiben. Ich hoffe aber, dass das Geschriebene dir wenigstens grob zeigt, wie man bei nicht allzu speziellem Anforderungen relativ schnell zu einem gewünschten Transistortyp kommt, der nicht nur die technischen Anforderungen erfüllt, sondern auch leicht beschaffbar ist.&lt;br /&gt;
&lt;br /&gt;
Werden die Anforderungen spezieller, helfen oft die Selektionstabellen auf den Webseiten der einschlägigen  Hersteller weiter. Auch Händler wie Farnell haben teilweise ganz gute Auswahlwerkzeuge. &lt;br /&gt;
&lt;br /&gt;
Wenn du dich intensiv mit Elektronik beschäftigst, wirst du wahrscheinlich noch viele  Schaltungen von Leuten zu Gesicht bekommen, die vielleicht schon etwas weiter fortgeschritten sind. Dabei wirst du immer wieder auf bestimmte Standardtypen von Transistoren (und auch anderen Bauteilen wie Operationsverstärker u.ä.) stoßen und sehen, welche [[Standardbauelemente]] &amp;quot;man&amp;quot; üblicherweise für bestimmte Anwendungen einsetzt. Mit der Zeit setzt sich dann eine Auswahl von bspw. 10 oder 20 verschiedenen Transistoren und 5 bis 10 verschiedenen OpAmps im Kopf fest, von denen man die wesentlichen Parameter auswendig kennt, so dass man ohne aufwendige Suche eine schnelle Auswahl treffen kann. Auch hier in der Artikelsammlung gibt es eine solche [[Transistor-Übersicht]].&lt;br /&gt;
&lt;br /&gt;
=== Wo ist die Antwort auf meine Frage? ===&lt;br /&gt;
&lt;br /&gt;
Vielleicht im Forum? Falls du sie da findest, dann pack das ganze doch hier rein.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/topic/165580 Gegen Vcc oder GND schalten]&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Basiswiderstand]]&lt;br /&gt;
* [[Transistor-Übersicht]]&lt;br /&gt;
* [[AVR_Transistortester]] - neu (Weiterentwickelt von Karl-Heinz Kübbeler)&lt;br /&gt;
* [[AVR-Transistortester]] - alte (von Markus Frejek)&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
== Gehäusebauformen von Transistoren ==&lt;br /&gt;
&lt;br /&gt;
=== TO-92 ===&lt;br /&gt;
Ein kleiner Aufsatz über TO-92 Transistorgehäuse und Footprints findet sich unter: [[Media:TO-92-Gehaeuse_RevB2.pdf]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://de.wikipedia.org/wiki/transistor &amp;quot;Transistor&amp;quot; bei Wikipedia]&lt;br /&gt;
* [http://www.elektronik-kompendium.de www.elektronik-kompendium.de]&lt;br /&gt;
** [http://www.elektronik-kompendium.de/sites/bau/0201291.htm Elko/Transistor]&lt;br /&gt;
* http://www.elektronikinfo.de/strom/bipolartransistoren.htm&lt;br /&gt;
* http://www.ferromel.de/tronic_1870.htm&lt;br /&gt;
* [http://www.DieElektronikerseite.de Die Elektronikerseite] Lehrgang: Der Transistor - Ein Tausendsassa&lt;br /&gt;
* [http://www.infoplease.com/encyclopedia/science/transistor-types-transistors.html Transistortypen]&lt;br /&gt;
* [http://electronics-electrical.exportersindia.com/electronic-components/transistors.htm Transistoren Industrieunternehmen Geschäftsauflistungen]&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Transistor&amp;diff=107011</id>
		<title>Transistor</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Transistor&amp;diff=107011"/>
		<updated>2024-07-11T10:10:14Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* Gibt es einen IC, der wie mehrere Transistoren funktioniert? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kunstwort aus &amp;quot;transfer resistor&amp;quot;, was etwa so viel bedeutet wie &amp;quot;übertragener [[Widerstand]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In den 1950-ern als praktische Anwendung des [[Halbleiter]]-Effekts erfundenes &amp;quot;solid state&amp;quot; Schalt- und Verstärkerelement, welches sehr klein ist, ohne bewegte Teile auskommt (anders als ein klassisches Relais) und keine energiefressende Heizung benötigt (anders als eine Röhre).&lt;br /&gt;
&lt;br /&gt;
Vom &amp;quot;bipolaren Transistor&amp;quot; (PNP, NPN) weiterentwickelt zum &amp;quot;Feldeffekt-Transistor&amp;quot; ([[FET]]), der heute - gefertigt mit einem preiswerten Verfahren unter Verwendung von Metall-Oxid-Schichten (MOS) - ein wesentliches Element integrierter Schaltkreise (ICs, integrated circuits) darstellt, und damit natürlich auch von [[Mikrocontroller]]n, um die es in diesem Wiki hauptsächlich geht (bzw. gehen sollte).&lt;br /&gt;
&lt;br /&gt;
== Schaltzeichen ==&lt;br /&gt;
[https://electronicsclub.info/images/transbce.gif]&lt;br /&gt;
&lt;br /&gt;
* E: Emitter&lt;br /&gt;
* B: Basis&lt;br /&gt;
* C: Collector    &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In ASCII Schaltplänen sehen Transistoren so aus:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
                       |&amp;gt;                 |/&lt;br /&gt;
NPN      |&amp;gt;   oder    -|       oder      -|&lt;br /&gt;
                       |\                 |&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                       |&amp;lt;                 |/&lt;br /&gt;
PNP:     |&amp;lt;   oder    -|       oder      -|&lt;br /&gt;
                       |\                 |&amp;lt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um zu erkennen, ob ein NPN oder PNP Transistor im Schaltplan verwendet wird, gibt es Eselsbrücken:&lt;br /&gt;
*Für Dichter: &#039;&#039;&#039;Tut der Pfeil der Basis weh, handelt&#039;s sich um PNP.&#039;&#039;&#039;&lt;br /&gt;
*Für Praktiker: &#039;&#039;&#039;PNP heisst &amp;quot;Pfeil Nach Platte&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
*Mit Dialekt: &#039;&#039;&#039;NPN &amp;quot;&#039;naus, Pfeil, &#039;naus&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
*..und für Gleichberechtigungsverfechter: &#039;&#039;&#039;NPN means &amp;quot;Not Pointing iN&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
JFET: [[Bild:Transistor_JFET.png]]&lt;br /&gt;
&lt;br /&gt;
MOSFET: [[Bild:Transistor_MOSFET.png]]&lt;br /&gt;
&lt;br /&gt;
* S: Source&lt;br /&gt;
* G: Gate&lt;br /&gt;
* D: Drain&lt;br /&gt;
&lt;br /&gt;
Eigentlich haben MOSFETs noch einen vierten Anschluss namens Bulk. Der ist aber nur bei Spezialtypen als Pin herausgeführt. Im Normalfall kann man ihn vergessen, da er nicht gesondert beschaltet werden muss, er ist praktisch und auch im Symbol mit Source verbunden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Die NPN/PNP Eselsbrücken funktionieren bei FETs nicht, denn bei einem P-Kanal FET zeigt der Pfeil weg vom FET!&lt;br /&gt;
Ein Bipolartransistor, im Englischen als bipolar junction transistor (BJT) bezeichnet, ist ein Transistor, bei dem Ladungsträger – negativ geladene Elektronen und positiv geladene Defektelektronen – zum Stromtransport durch den Bipolartransistor beitragen. Der BJT wird mittels eines elektrischen Stroms gesteuert und wird zum Schalten und Verstärken von Signalen ohne mechanisch bewegte Teile eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Bipolare Leistungstransistoren sind für das Schalten und Verstärken von Signalen höherer Stromstärken und Spannungen ausgelegt.&lt;br /&gt;
&lt;br /&gt;
== Typbezeichnungen == &lt;br /&gt;
&lt;br /&gt;
Neben den Typbezeichnungen wie 2Nxxxx, TIPxxx, MJxxx, MJExx gibt es noch die in Europa geläufigere  Kennzeichnung bestehend aus zwei Buchstaben und drei Ziffern. Die diversen Kennzeichnungsmöglichkeiten sind in einem eigenen Artikel ([[Kennzeichnung von Halbleitern]]) zusammengefasst.&lt;br /&gt;
&lt;br /&gt;
== Kenndaten/Parameter ==&lt;br /&gt;
&lt;br /&gt;
Im [http://www.mikrocontroller.net/topic/197676#1938546 Beitrag: Transistorparameter Erklärung] sind Links zu Erläuterungen spezieller Kürzel.&lt;br /&gt;
&lt;br /&gt;
== Transistor Grundschaltungen ==&lt;br /&gt;
&lt;br /&gt;
Generell gilt, dass Strom vom Kollektor zum Emitter nur dann fließen kann, wenn die Basis positiver (NPN) bzw. negativer (PNP) wird als der Emitter. Dabei darf die Basis nicht direkt mit Vcc (NPN) oder GND (PNP) verbunden werden, da der Basisstrom sonst zu gross wird. Es muss jeweils ein geeigneter Basiswiderstand (R_Basis) gewählt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* Artikel [[Basiswiderstand]]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0203111.htm Transistor Grundschaltungen im ElKo]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Transistor Transistor bei RoboterNetz.de]&lt;br /&gt;
&lt;br /&gt;
Es gibt drei Grundschaltungen. Der Name beschreibt den Anschluss, welcher sich auf einem festen Potential (Spannung) befindet. Die beiden anderen Anschlüsse haben bedingt durch die Schaltung ein veränderliches Potential.&lt;br /&gt;
&lt;br /&gt;
=== Kollektorschaltung (Emitterfolger)===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anwendung:&#039;&#039;&#039;&lt;br /&gt;
* Impedanzwandler&lt;br /&gt;
* Darlington-Schaltung&lt;br /&gt;
* Schalter&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Keine Phasendrehung&lt;br /&gt;
* Hohe Stromverstärkung&lt;br /&gt;
* Keine Spannungsverstärkung&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel: Transistor als Schalter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* NPN: Kollektor mit Vcc verbinden, Last an Emitter&lt;br /&gt;
* PNP: Kollektor mit GND verbinden, Last an Emitter&lt;br /&gt;
&lt;br /&gt;
In diesem Fall regelt der Transistor die Spannungen am Emitter, daher wird die Last am Emitter angeschlossen. Die Spannung am Emitter entspricht immer der an der Basis minus 0,6V, sie folgt der Basisspannung, deswegen auch der Name Emitterfolger. Daher ist diese Schaltung nicht geeignet, um 12V mit 5V zu schalten. &lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[Bild:NPN_Collector.gif | framed | center | NPN Transistor in Kollektorschaltung]] &lt;br /&gt;
|| [[Bild:PNP_Collector.gif | framed | center | PNP Transistor in Kollektorschaltung]]  &lt;br /&gt;
|}&lt;br /&gt;
* NPN: Wird &amp;lt;math&amp;gt;R_{Poti}&amp;lt;/math&amp;gt; (Spannungsteiler) erhöht, &#039;&#039;&#039;steigt&#039;&#039;&#039; die Spannung &amp;lt;math&amp;gt;U_{Last}&amp;lt;/math&amp;gt; letztlich bis auf VCC-0,6V (Basis-Emitter-Übergang).&lt;br /&gt;
* PNP: Wird &amp;lt;math&amp;gt;R_{Poti}&amp;lt;/math&amp;gt; (Spannungsteiler) erhöht, &#039;&#039;&#039;sinkt&#039;&#039;&#039; Spannung an &amp;lt;math&amp;gt;U_{Last}&amp;lt;/math&amp;gt; letztlich bis auf 0,6V (Basis-Emitter-Übergang).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0204133.htm Kollektorschaltung im ElKo]&lt;br /&gt;
&lt;br /&gt;
=== Emitterschaltung ===&lt;br /&gt;
&lt;br /&gt;
Die Emitterschaltung bietet hohe Spannungs- und Stromverstärkung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anwendung:&#039;&#039;&#039;&lt;br /&gt;
* NF- und HF-Verstärker&lt;br /&gt;
* Leistungsverstärker&lt;br /&gt;
* Transistor als Schalter&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Phasendrehung 180°&lt;br /&gt;
* Hohe Spannungsverstärkung&lt;br /&gt;
* Hohe Stromverstärkung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel: Transistor als Schalter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Last liegt am Kollektor. Der Strom durch den Schalter oder an U_Schalt steuert den Strom zwischen Kollektor und Emitter. Wird der Schalter geschlossen, fließt ein Strom.&lt;br /&gt;
&lt;br /&gt;
[[Bild:NPN_Schalter.gif | framed | center | NPN Transistor als Schalter]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                   ___                                          ___&lt;br /&gt;
Vcc/+ o-----------------------+          Vcc/+ o-----------------------+&lt;br /&gt;
                              |                                        |&lt;br /&gt;
                     ___    |&amp;lt;            U_schalt (-)       ___     |&amp;lt;  PNP             &lt;br /&gt;
             +------|___|---|  PNP             o------------|___|----|&lt;br /&gt;
             |     R_Basis  |\                             R_Basis   |\&lt;br /&gt;
    Schalter \                |                                        |&lt;br /&gt;
             |       ___      |                               ___      |&lt;br /&gt;
GND/- o------+------|___|-----+          GND/- o-------------|___|-----+&lt;br /&gt;
                   R_Last                                    R_Last     &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0204302.htm Emitterschaltung im ElKo]&lt;br /&gt;
&lt;br /&gt;
=== Basisschaltung ===&lt;br /&gt;
&lt;br /&gt;
Die Basisschaltung findet sich vor allem in Eingangsstufen in der HF-Technik. Im Schaltbetrieb wird sie praktisch nur zur [[Pegelwandler#STEP-UP: 5V -&amp;gt; 9..15V | Pegelwandlung]] für nachfolgende Stufen verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Geringe Eingangsimpedanz&lt;br /&gt;
* Keine Phasenverschiebung&lt;br /&gt;
* Hohe Bandbreite&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0205081.htm Basisschaltung im ElKo&lt;br /&gt;
&lt;br /&gt;
== FAQ aus dem Forum ==&lt;br /&gt;
&lt;br /&gt;
=== PNP/NPN als Schalter, wohin mit der Last? === &lt;br /&gt;
Für viele einfache Anwendungen kann man sich merken: &#039;&#039;&#039;Bei Schaltanwendungen darf der Basisstrom nicht durch die Last fließen&#039;&#039;&#039; (… wenn die zu schaltende Spannung höher ist als die Steuerspannung). Normalerweise kommt dabei die Emitterschaltung zum Einsatz, die Last kommt also an den Kollektor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 Ucc o─────────────┐                  Ucc o───────────┐&lt;br /&gt;
                   │                                  │&lt;br /&gt;
                  ┏┷┓               An: GND   ___   ┃&amp;lt;&lt;br /&gt;
                  ┃ ┃ R_Last             o───┤___├──┨   PNP&lt;br /&gt;
                  ┗┯┛               Aus: Ucc        ┃\&lt;br /&gt;
                   │                                  │&lt;br /&gt;
An: Ucc  ___     ┃/                                  ┏┷┓&lt;br /&gt;
    o───┤___├────┨   NPN                             ┃ ┃ R_Last&lt;br /&gt;
Aus: GND         ┃&amp;gt;                                  ┗┯┛&lt;br /&gt;
                   │                                  │&lt;br /&gt;
 GND o─────────────┘                  GND o───────────┘&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe [[Basiswiderstand]] zur Berechnung des notwendigen Basiswiderstandes bei gegebener Last R_Last für einen Transistor als Schalter.&lt;br /&gt;
&lt;br /&gt;
Siehe auch Threads im Forum: &lt;br /&gt;
* http://www.mikrocontroller.net/topic/58567 &lt;br /&gt;
* http://www.mikrocontroller.net/topic/119841&lt;br /&gt;
* oder im [http://www.elektronik-kompendium.de/sites/slt/0208031.htm Elektronik-Kompendium-Forum]&lt;br /&gt;
&lt;br /&gt;
Ist die zu schaltende Spannung &#039;&#039;&#039;genauso hoch&#039;&#039;&#039; wie die Steuerspannung darf die Last an den Emitter, und der Basiswiderstand entfällt. Der Transistor arbeitet als &#039;&#039;Impedanzwandler&#039;&#039; und steigert die Treiberfähigkeit des Mikrocontroller-Ausgangs in den Ampere-Bereich. Dieser Trick funktioniert &#039;&#039;&#039;nur&#039;&#039;&#039; mit Bipolartransistoren.&lt;br /&gt;
&lt;br /&gt;
Ist die effektive Invertierung des Schaltzustandes im oben angegebenen Bild lästig &#039;&#039;&#039;und&#039;&#039;&#039; der Strom geringer als der Ausgangsstrom, kann man den Transistor am Emitter steuern. Eine solche Schaltung funktioniert noch besser mit (Logik-Level-)MOSFETs und stellt eine einfache Kopplung zwischen Systemen mit unterschiedlicher Betriebsspannung (bspw. 1,8 V und 3,3 V) her. MOSFETs werden dazu am Source-Pin gesteuert, während das Gate an der „gegenüberliegenden“ Speisespannung „fixiert“ wird.&lt;br /&gt;
&lt;br /&gt;
=== Wie kann ich mit 5V vom Mikrocontroller 12V und mehr schalten? === &lt;br /&gt;
Schau mal hier:&lt;br /&gt;
* Wikiartikel [[Pegelwandler]]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/praxis/bausatz_pegelwandler-mit-transistoren.htm Pegelwandler im ElKo]&lt;br /&gt;
* [http://dl6gl.de/grundlagen/schalten-mit-transistoren schalten-mit-transistoren]&lt;br /&gt;
&lt;br /&gt;
oder in diesen Threads:&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/17899 Transistor als Schalter]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/14437 Vcc schalten mit MOSFET]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/29830 Schalten mit PNP-Transistor]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/104027 Transistorschalter für Versorgungsspannung]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/104951#921417 7-50V strombegrenzt schalten]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/103116#900247 P-Kanal MOSFET ansteuern]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/383039#4367479 5V/15mA mit 3,3V schalten]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/401317#4638354 High Side Treiber mit Ladungspumpe]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/347885?goto=3855574#3855574 minus -12V mit 3,3V schalten]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/516373?goto=6650986#6650986 Kurzschlußfester Schalter für VCC]&lt;br /&gt;
&lt;br /&gt;
Bei der Kollektor-Schaltung entspricht die Spannung am Emitter immer der an der Basis, daher ist sie nur bedingt geeignet. Zum Schalten können die folgenden Emitter-Schaltungen verwendet werden. Achtung: In der zweiten davon arbeitet T1 in Basisschaltung.&lt;br /&gt;
&lt;br /&gt;
Schalten gegen GND&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 +12V o------------------------+&lt;br /&gt;
                               |&lt;br /&gt;
                              .-. &lt;br /&gt;
                             ( X )  &lt;br /&gt;
                              &#039;-&#039;&lt;br /&gt;
                               |&lt;br /&gt;
                    ___      |/ T1,NPN   &lt;br /&gt;
        uC PIN o---|___|-----| BC547     &lt;br /&gt;
                   R2,4K7    |&amp;gt;&lt;br /&gt;
                               |&lt;br /&gt;
  GND o---------o--------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Schalten gegen +12V&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 +12V o--------------+----------------------+&lt;br /&gt;
                     |                      |&lt;br /&gt;
                     |   ____              |&amp;lt; T2, PNP&lt;br /&gt;
                     +--|____|----+--------|  BC557&lt;br /&gt;
                        R1,4K7    |        |\&lt;br /&gt;
                                |/T1,NPN    |&lt;br /&gt;
         Vcc/+5V o--------------| BC547     |&lt;br /&gt;
                                |&amp;gt;          |&lt;br /&gt;
                        ___       |        .-. &lt;br /&gt;
          uC PIN o-----|___|------+       ( X )  &lt;br /&gt;
                       R2,4K7              &#039;-&#039;&lt;br /&gt;
                                            |&lt;br /&gt;
  GND o----------o--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Transistor an µC ohne Vorwiderstand === &lt;br /&gt;
&lt;br /&gt;
Normalerweise sind IO Pins vom µC nicht in der Lage, große Ströme zu treiben, beim AVR maximal ~20mA. Für einen kleinen Transistor ist das immer noch zu viel und es wäre auch Stromverschwendung.&lt;br /&gt;
&lt;br /&gt;
Deshalb kann man den IO-Pin des AVRs einfach als Tristate Eingang einstellen (Portpin als Eingang und Pullup deaktivieren), damit kein Basisstrom fließt.&lt;br /&gt;
&lt;br /&gt;
Aktiviert man nun den internen Pullup-Widerstand des AVRs, agiert dieser als Basisvorwiderstand, und es fließt nur ein geringer Basisstrom (die Pullups eines AVRs liegen irgendwo bei 50k bis 100k Ohm - siehe Datenblatt).&lt;br /&gt;
&lt;br /&gt;
Nur sollte man bei kleinen Transistoren aufpassen, dass man den Portpin in der Software nie als aktiven Ausgang schaltet. &lt;br /&gt;
&lt;br /&gt;
Wenn der verwendete µC zuschaltbare Pulldown-Widerstände an seinen Pins besitzt, kann man das gleiche auch mit einem PNP-Transistor machen (natürlich nur den Pulldown aktivieren).&lt;br /&gt;
&lt;br /&gt;
Eine Anwendung wären z.&amp;amp;nbsp;B. Nixie-Röhren-Kathodentreiber (geringe Stromverstärkung nötig).&lt;br /&gt;
&lt;br /&gt;
=== Wann bipolare (NPN/PNP) und wann FETs (insbesonders, wenn LEDs im Spiel sind)?=== &lt;br /&gt;
Oft sind bipolare Transistoren (NPN/PNP) schon ausreichend, vor allem wenn &amp;quot;normale&amp;quot; LEDs (20mA) verwendet werden. FETs sind u.a. dann gut, wenn mit geringen Eingangsströmen hohe Ausgangsströme (über 300 mA) geschaltet werden sollen, also bei den Power-LEDs (Luxeon...).&lt;br /&gt;
&lt;br /&gt;
Ein Grenzfall: 500mA/5V schalten, siehe http://www.mikrocontroller.net/topic/62327.&lt;br /&gt;
&lt;br /&gt;
=== Wieso gehen bei einer Multiplex-Anzeige mit Schieberegister 74HC595 und (Darlington-)Transistor als Zeilentreiber die LED nicht ganz aus?  ===&lt;br /&gt;
Das liegt an der &#039;&#039;&#039;Miller-Kapazität&#039;&#039;&#039; des übersteuerten (Darlington-)Transistors. Der braucht erst mal einige 10µs, um zu sperren. Du mußt also erstmal beide Zeilen ausschalten, dann etwas warten und dann die nächste Zeile an. Oder Du ersetzt die Darlington durch P-FETs. [http://www.mikrocontroller.net/topic/132545]&lt;br /&gt;
&lt;br /&gt;
=== Wie steuert man ein Relais? ===&lt;br /&gt;
Normalerweise verwendet man zur Ansteuerung von Relais NPN-Transistoren in Emitterschaltung. Freilaufdiode nicht vergessen! &lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Relais mit Logik ansteuern]]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/22023/Relaisanteuerung.png Schaltbilder aus dem Forum]&lt;br /&gt;
&lt;br /&gt;
=== Was ist die Spannung &amp;lt;math&amp;gt;U_{BE\_sat}&amp;lt;/math&amp;gt; (lt. Datenblatt max. 1,2V)? ===&lt;br /&gt;
Bekanntlich verhält sich die Basis-Emitter Strecke eines Transistors wie eine Diode und &amp;lt;math&amp;gt;U_{BE\_sat}&amp;lt;/math&amp;gt; ist die bei maximal zulässigem Basisstrom anliegende Vorwärtsspannung.&lt;br /&gt;
&lt;br /&gt;
=== Was bewirkt ein Kondensator (100µF-1nF) parallel zur Basis-Emitter-Strecke nach Masse? === &lt;br /&gt;
Er wirkt mit dem Basisvorwiderstand als RC-Tiefpass. Damit wird der Transistor eigentlich nicht mehr als Schalter, sondern als Linearregler betrieben. Manche Verstärker-Schaltungen sind, gerade bei hohen Lasten, sehr schwingfreudig. Deswegen ist bei PWM so ein C nicht sinnvoll.&lt;br /&gt;
&lt;br /&gt;
=== Gibt es einen IC, der wie mehrere Transistoren funktioniert? ===&lt;br /&gt;
&lt;br /&gt;
Gibt es! Beispielsweise der &#039;&#039;&#039;ULN2803&#039;&#039;&#039; ist ein 8-fach Darlington Transistor Array mit [[Ausgangsstufen_Logik-ICs#Open_Collector|Open-Collector Ausgang]]. Und sogar die Freilaufdioden sind schon drin.&lt;br /&gt;
Damit lässt sich z.&amp;amp;nbsp;B. ein Leistungstreiber zur Ansteuerung von Schrittmotoren, Relais und anderen induktiven Lasten aufbauen.&lt;br /&gt;
&lt;br /&gt;
=== Gibt es ein Transistor-Array wie ULN28xx, das gegen Vcc schaltet? ===&lt;br /&gt;
&lt;br /&gt;
Such mal nach UDN29xx, z.&amp;amp;nbsp;B. UDN2981, UDN2987 ...&lt;br /&gt;
&lt;br /&gt;
Allegro hat zwischenzeitlich seinen Source-Treiber umbenannt. Die Bauteile heißen jetzt A2981, A2982 usw... Die Datenblätter sind identisch geblieben.&lt;br /&gt;
&lt;br /&gt;
Nachteil eines Darlington-Source-Treibers ist die hohe Sättigungsspannung (VceSat) von typisch 1,2 V. Die Eingangsspannung sollte dementsprechend höher ausgelegt werden oder gleich ein Source-Treiber mit MOSFET-Transistoren gewählt werden (z.B. Infineon ProFETs).&lt;br /&gt;
&lt;br /&gt;
=== Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein? ===&lt;br /&gt;
siehe [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
Die Summe allen Übels ist konstant. Man muss wissen, welches Bauteil sich wofür besonders eignet.&lt;br /&gt;
&lt;br /&gt;
====[[FET | MOSFET]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Bei niedrigen Spannungen &amp;lt;100V sehr gut geeignet, &amp;lt;200V gut geeignet, sehr geringe R_DS-ON Widerstände möglich (einstelliger mOhm-Bereich)&lt;br /&gt;
* Hohe Schaltgeschwindigkeiten möglich&lt;br /&gt;
* Geringe An- und Ausschaltverluste&lt;br /&gt;
* Statisch praktisch leistungslos steuerbar&lt;br /&gt;
* Bodydiode kann als Freilaufdiode in H-Brücken verwendet werden&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Drain-Source Sperrspannung zerstört das Bauteil nicht, wenn der Strom sowie die Energie begrenzt werden. (Verhalten wie [[Diode | Z-Diode]])&lt;br /&gt;
   &lt;br /&gt;
Nachteile&lt;br /&gt;
*Antiparallele Diode (Bodydiode) ist in nahezu allen MOSFETs unvermeidlich, daduch Sperren nur in einer Polarität möglich, Stromfluss über den MOSFET aber in beiden Richtungen möglich (Inversbetrieb, Synchrongleichrichter)&lt;br /&gt;
* Bei Sperrspannungen &amp;gt;100V deutlich steigende Einschaltwiderstände (R_DS-ON)&lt;br /&gt;
* Schnelles Umschalten erfordert hohe Lade-und Entladeströme ([[MOSFET-Übersicht#Mosfet-Treiber|MOSFET-Treiber]])&lt;br /&gt;
* Leitverluste quadratisch proportional zum Strom, Pv = I²*R_DS-ON&lt;br /&gt;
&lt;br /&gt;
====[[Transistor | Bipolartransistor]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Hohe Spannungsfestigkeiten möglich, bis zu 2000V&lt;br /&gt;
* Sehr hohe Schaltgeschwindigkeiten möglich&lt;br /&gt;
* Leitverluste etwa linear proportional zum Strom und Kollektor-Emitter-Sättigungsspannung, Pv = I * Uce-sat, U_CEC-sat typ 0,1...2.5 V&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Stromgesteuert, damit wird immer eine gewisse Ansteuerleistung benötigt&lt;br /&gt;
* Bei grossen Kollektorströmen nimmt die Stromverstärkung deutlich ab, dann wird ein großer Basisstrom benötigt&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Kollektor-Emitter Sperrspannung zerstört das Bauteil&lt;br /&gt;
&lt;br /&gt;
====[[IGBT]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Hohe Spannungsfestigkeiten möglich, bis zu 6600V, in üblichen Bauformen bis ca 1700V gut verfügbar&lt;br /&gt;
* Statisch praktisch leistungslos steuerbar&lt;br /&gt;
* Mit oder ohne antiparallele Diode zur Kollektor-Emitter-Strecke verfügbar&lt;br /&gt;
* Leitverluste linear proportional zum Strom und Kollektor-Emitter-Sättigungsspannung, Pv = I * Uce-sat, U_CE-sat typ. 2V&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Nur mäßige Schaltfrequenzen möglich (typ. &amp;lt;30..50kHz)&lt;br /&gt;
* Schnelles Umschalten erfordert hohe Lade-und Entladeströme ([[MOSFET-Übersicht#Mosfet-Treiber|MOSFET-Treiber]])&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Kollektor-Emitter Sperrspannung zerstört das Bauteil&lt;br /&gt;
&lt;br /&gt;
====[[TRIAC]]/Thyristor====&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Sehr hohe Spannungsfestigkeiten möglich (800V...mehrere kV, Thyristoren bis 12kV)&lt;br /&gt;
* Mit kurzen Pulsen einschaltbar, danach Selbsthaltung des Stromflusses&lt;br /&gt;
* Leitverluste linear proportional zum Strom, Pv = I * Uak, Uak typ. 0,8...1,5 V &lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Auf niedrige Schaltfrequenzen beschränkt (kHz-Bereich, Schaltzeit im Bereich von µs)&lt;br /&gt;
* Anstiegsgeschwindigkeit des Stromes muss begrenzt werden, sonst kommt es zu Bauteilschäden&lt;br /&gt;
* Anstiegsgeschwindigkeit der Spannung muss begrenzt werden, sonst kommt es zum ungewollten Zünden&lt;br /&gt;
* Stromfluss kann nicht ausgeschaltet werden, damit meist nur Einsatz an Wechselspannung&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50em&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Bauteil               &lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | optimales&amp;lt;br&amp;gt;Einsatzgebiet&lt;br /&gt;
! Kommentar&lt;br /&gt;
|-&lt;br /&gt;
| MOSFET                || 0..200V, 0..500A || im Kleinspannungsbereich meist die beste Wahl als Schalter&lt;br /&gt;
|-&lt;br /&gt;
| Bipolartransistor     || 0..1000V, 0..10A || wird mehr und mehr von MOSFETs verdrängt&lt;br /&gt;
|-&lt;br /&gt;
| IGBT                  || 200..1700V, 0..500A || optimal für hohe Spannungen und hohe Ströme&lt;br /&gt;
|-&lt;br /&gt;
| Triac/Thyristor       || 230V, 400V, 680V,&amp;lt;br&amp;gt;bis mehrere kV, 0..100A || meist für Wechselspannung, &amp;lt;br&amp;gt;Thyristoren bis 1000A im Dauerbetrieb, &amp;lt;br&amp;gt;im Pulsbetrieb einige kA. (Scheibenzelle)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Wie finde ich den richtigen Transistor für eine LED-Ansteuerung? ===&lt;br /&gt;
&lt;br /&gt;
Quelle: Beiträge [http://www.mikrocontroller.net/topic/157763#1493623] und [http://www.mikrocontroller.net/topic/157763#1494972] von yalu&lt;br /&gt;
&lt;br /&gt;
Um am Anfang wenigstens ein bisschen den Durchblick im Transistordschungel zu behalten, kannst du folgendermaßen vorgehen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nomenklatur&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nach der amerikanischen Nomenklatur beginnen die Transistornamen meist mit 2N (z.&amp;amp;nbsp;B. 2N2222 oder 2N3055) und nach der japanischen mit 2S (z.&amp;amp;nbsp;B. 2SC1815). Für den Anfang kann man sich auf europäische Transistoren beschränken, da es diese in ausreichender Auswahl gibt und die Bezeichnungen relativ gut den Transistortyp wiedergeben:&lt;br /&gt;
&lt;br /&gt;
Der erste Buchstabe bezeichnet das Halbleitermaterial (A=Germanium, B=Silizium). Germaniumtransistoren werden heute nur noch selten verwendet.&lt;br /&gt;
&lt;br /&gt;
Der zweite Buchstabe steht für den Einsatzzweck (C=Universal, D=hohe Leistung, F=Hochfrequenz, U=hohe Spannung).&lt;br /&gt;
&lt;br /&gt;
So ist also ein ACxxx ein Germaniumuniversaltransistor und ein BDxxx ein Siliziumleistungstransistor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auswahl&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn du dich für einen Grundtyp entschieden hast (für die LED ist ein BC-Typ das Richtige), gehst du auf die Webseite eines Elektronikhändlers (Reichelt, Kessler usw.), schlägst die Seite mit den BC-Transistoren auf. Da gibt es natürlich sehr viele  davon, und du brauchst jetzt eine&lt;br /&gt;
Suchreihenfolge. Als erstes Auswahlkriterium nimmst du den Preis, denn:&lt;br /&gt;
&lt;br /&gt;
* Zuviel Geld hast wahrscheinlich nicht einmal du.&lt;br /&gt;
* Billig ist meist das, was in großen Stückzahlen hergestellt wird. Was für die Masse gut ist, ist (zumindest in diesem Fall) meist auch für dich gut.&lt;br /&gt;
* Was billig und damit in Massen verkauft wird, bekommst du auch bei anderen Händlern und auch noch in 10 Jahren. Das ist wichtig, wenn deine Schaltung irgendwann einmal in Serie gefertigt werden soll.&lt;br /&gt;
&lt;br /&gt;
Gleich als nächstes überlegst du, ob du einen NPN- oder einen PNP-Typ brauchst. Das ergibt sich aus der Anordnung der Bauteile in deiner Schaltung. Hast du die Möglichkeit, die Schaltung wahlweise für einen NPN- oder einen PNP-Typ auszulegen, wählst du die Variante mit dem NPN-Typ. Um einfach eine LED über einen Mikrocontroller einzuschalten, ist i.Allg. ein NPN-Typ in Emitterschaltung richtig.&lt;br /&gt;
&lt;br /&gt;
Ein weiteres wichtiges Kriterium ist die Befestigungstechnik: Wenn dir die SMD-Löterei etwas suspekt ist, lässt du die entsprechenden Modelle erst einmal alle außen vor. Ein typisches Nicht-SMD-Gehäuse für Universaltransistoren ist TO-92. Es gibt im Internet bebilderte Listen mit den einzelnen Gehäuseformen ([[IC-Gehäuseformen#Weblinks]])&lt;br /&gt;
&lt;br /&gt;
Wenn du jetzt also bei Reichelt  die BC-Transistoren nach Preis aufsteigend sortiert hast, siehst du erst einen Schwung SMD-Tranistoren. Dann kommen ein paar Transistoren im TO-92-Gehäuse, die sind aber PNP. Etwas weiter unten kommt der erste NPN-Transistor in TO-92, nämlich der BC547C. Netterweise stehen gleich ein paar Eckdaten dabei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
BC547C  45V  0,1A  0,5W&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die 45V sind die maximale Kollektor-Emitter-Spannung, in deinem Fall also die Spannung, die du maximal schalten kannst. Da die LED bei Weitem keine 45V braucht und deine Versorgungsspannung eher in der Gegend von 5V liegt, bist du auf jeden Fall auf der sicheren Seite.&lt;br /&gt;
&lt;br /&gt;
Deine LED wird typisch mit 20mA (max. 30mA) betrieben. Der BC547C kann 100mA, also ist auch hier noch Luft.&lt;br /&gt;
&lt;br /&gt;
Zur maximalen Verlustleistung (0,5W): Wenn deine LED eingeschaltet ist, fließen bspw. 20mA. Ist der Transistor voll durchgesteuert (in Sättigung) beträgt die Kollektor-Emitter-Spannung bei diesem geringen Strom typischerweise zwischen 0,1V und 0,2V (Genaueres steht im Datenblatt). Am Transistor wird also maximal die Leistung 20mA·0,2V=4mW in Wärme umgesetzt. Bis zu 500mW dürfen es sein, also ebenfalls ok.&lt;br /&gt;
&lt;br /&gt;
Nachdem du den Transistor in engere Auswahl gezogen hast, lohnt sich auf jeden Fall ein Blick ins Datenblatt. Aus den Tabellen und Diagrammen erfährst du bspw., wie hoch der Basisstrom sein muss, um den Kollektorstrom von mindestens 20mA bei ausreichend geringer CE-Spannung bereitzustellen. Dort ist auch erklärt warum es einen BC547A, BC547B und BC547C gibt. Der letzte Buchstabe gibt nämlich die Stromverstärkungsklasse an. Da eine hohe Stromverstärkung meist wünschenswert ist und in diesem Fall keinen Aufpreis kostet, ziehst du den BC547C den anderen beiden vor.&lt;br /&gt;
&lt;br /&gt;
Da in deiner Anwendung HF- und Rauschverhalten keine Rolle spielen, bist du schon am Ziel angelangt.&lt;br /&gt;
&lt;br /&gt;
Würde deine LED 100mA statt 20mA benötigen, wären die max. 100mA des BC547 etwas knapp bemessen. Du blätterst also in der Reichelt-Liste weiter und stößt auf den BC337-40 mit 45V, 0,5A und 0,525W. Das ist genau das, wonach du suchst. Bei diesem Transistor sind die Stromverstärkungsklassen durch die Endungen -16, -25 und -40 gekennzeichnet. Es wäre ja auch&lt;br /&gt;
zu einfach, wenn immer nur A, B und C verwendet würde ;-)&lt;br /&gt;
&lt;br /&gt;
Bei Strömen ab etwa 500mA kommt man an die Grenze der Leistungsfähigkeit der BC-Typen. Dann geht es weiter mit BD. Der BD135 geht bspw. schon bis 1,5A. Das Problem bei solchen größeren Transistoren: Die Stromverstärkung ist nicht besonders hoch, so dass irgendwann der Mikrocontroller nicht mehr den benötigten Basisstrom liefern kann. Dann muss dem großen Transistor ein kleiner vorangeschaltet werden, um den erhöhten Basisstrom bereitszustellen. Man kann diese Kombination von zwei Transistoren auch fertig als Darlington-Transistor kaufen, von denen ebenfalls einige in der BD-Reihe zu finden sind (z.&amp;amp;nbsp;B. BD647). Ein Transistortyp, der sich sehr gut zum Schalten höherer Ströme eignet, ist der [[FET | MOSFET]].&lt;br /&gt;
&lt;br /&gt;
Wie schon oben angedeutet: Wenn die 30-80V die die meisten BC- und BD-Transistoren abkönnen, nicht ausreichen, suchst du weiter bei BU.&lt;br /&gt;
&lt;br /&gt;
Steigst du in die HF-Technik ein, sind BF-Transistoren eher das Richtige, wobei bei HF-Anwendungen die Auswahl der Transistoren nicht mehr das Schwierigste ist ;-)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Und wie geht&#039;s weiter?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Man könnte natürlich noch viel mehr zu diesem Thema schreiben. Ich hoffe aber, dass das Geschriebene dir wenigstens grob zeigt, wie man bei nicht allzu speziellem Anforderungen relativ schnell zu einem gewünschten Transistortyp kommt, der nicht nur die technischen Anforderungen erfüllt, sondern auch leicht beschaffbar ist.&lt;br /&gt;
&lt;br /&gt;
Werden die Anforderungen spezieller, helfen oft die Selektionstabellen auf den Webseiten der einschlägigen  Hersteller weiter. Auch Händler wie Farnell haben teilweise ganz gute Auswahlwerkzeuge. &lt;br /&gt;
&lt;br /&gt;
Wenn du dich intensiv mit Elektronik beschäftigst, wirst du wahrscheinlich noch viele  Schaltungen von Leuten zu Gesicht bekommen, die vielleicht schon etwas weiter fortgeschritten sind. Dabei wirst du immer wieder auf bestimmte Standardtypen von Transistoren (und auch anderen Bauteilen wie Operationsverstärker u.ä.) stoßen und sehen, welche [[Standardbauelemente]] &amp;quot;man&amp;quot; üblicherweise für bestimmte Anwendungen einsetzt. Mit der Zeit setzt sich dann eine Auswahl von bspw. 10 oder 20 verschiedenen Transistoren und 5 bis 10 verschiedenen OpAmps im Kopf fest, von denen man die wesentlichen Parameter auswendig kennt, so dass man ohne aufwendige Suche eine schnelle Auswahl treffen kann. Auch hier in der Artikelsammlung gibt es eine solche [[Transistor-Übersicht]].&lt;br /&gt;
&lt;br /&gt;
=== Wo ist die Antwort auf meine Frage? ===&lt;br /&gt;
&lt;br /&gt;
Vielleicht im Forum? Falls du sie da findest, dann pack das ganze doch hier rein.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/topic/165580 Gegen Vcc oder GND schalten]&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Basiswiderstand]]&lt;br /&gt;
* [[Transistor-Übersicht]]&lt;br /&gt;
* [[AVR_Transistortester]] - neu (Weiterentwickelt von Karl-Heinz Kübbeler)&lt;br /&gt;
* [[AVR-Transistortester]] - alte (von Markus Frejek)&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
== Gehäusebauformen von Transistoren ==&lt;br /&gt;
&lt;br /&gt;
=== TO-92 ===&lt;br /&gt;
Ein kleiner Aufsatz über TO-92 Transistorgehäuse und Footprints findet sich unter: [[Media:TO-92-Gehaeuse_RevB2.pdf]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://de.wikipedia.org/wiki/transistor &amp;quot;Transistor&amp;quot; bei Wikipedia]&lt;br /&gt;
* [http://www.elektronik-kompendium.de www.elektronik-kompendium.de]&lt;br /&gt;
** [http://www.elektronik-kompendium.de/sites/bau/0201291.htm Elko/Transistor]&lt;br /&gt;
* http://www.elektronikinfo.de/strom/bipolartransistoren.htm&lt;br /&gt;
* http://www.ferromel.de/tronic_1870.htm&lt;br /&gt;
* [http://www.DieElektronikerseite.de Die Elektronikerseite] Lehrgang: Der Transistor - Ein Tausendsassa&lt;br /&gt;
* [http://www.infoplease.com/encyclopedia/science/transistor-types-transistors.html Transistortypen]&lt;br /&gt;
* [http://electronics-electrical.exportersindia.com/electronic-components/transistors.htm Transistoren Industrieunternehmen Geschäftsauflistungen]&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Transistor&amp;diff=107010</id>
		<title>Transistor</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Transistor&amp;diff=107010"/>
		<updated>2024-07-11T10:07:30Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* PNP/NPN als Schalter, wohin mit der Last? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kunstwort aus &amp;quot;transfer resistor&amp;quot;, was etwa so viel bedeutet wie &amp;quot;übertragener [[Widerstand]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In den 1950-ern als praktische Anwendung des [[Halbleiter]]-Effekts erfundenes &amp;quot;solid state&amp;quot; Schalt- und Verstärkerelement, welches sehr klein ist, ohne bewegte Teile auskommt (anders als ein klassisches Relais) und keine energiefressende Heizung benötigt (anders als eine Röhre).&lt;br /&gt;
&lt;br /&gt;
Vom &amp;quot;bipolaren Transistor&amp;quot; (PNP, NPN) weiterentwickelt zum &amp;quot;Feldeffekt-Transistor&amp;quot; ([[FET]]), der heute - gefertigt mit einem preiswerten Verfahren unter Verwendung von Metall-Oxid-Schichten (MOS) - ein wesentliches Element integrierter Schaltkreise (ICs, integrated circuits) darstellt, und damit natürlich auch von [[Mikrocontroller]]n, um die es in diesem Wiki hauptsächlich geht (bzw. gehen sollte).&lt;br /&gt;
&lt;br /&gt;
== Schaltzeichen ==&lt;br /&gt;
[https://electronicsclub.info/images/transbce.gif]&lt;br /&gt;
&lt;br /&gt;
* E: Emitter&lt;br /&gt;
* B: Basis&lt;br /&gt;
* C: Collector    &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In ASCII Schaltplänen sehen Transistoren so aus:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
                       |&amp;gt;                 |/&lt;br /&gt;
NPN      |&amp;gt;   oder    -|       oder      -|&lt;br /&gt;
                       |\                 |&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                       |&amp;lt;                 |/&lt;br /&gt;
PNP:     |&amp;lt;   oder    -|       oder      -|&lt;br /&gt;
                       |\                 |&amp;lt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um zu erkennen, ob ein NPN oder PNP Transistor im Schaltplan verwendet wird, gibt es Eselsbrücken:&lt;br /&gt;
*Für Dichter: &#039;&#039;&#039;Tut der Pfeil der Basis weh, handelt&#039;s sich um PNP.&#039;&#039;&#039;&lt;br /&gt;
*Für Praktiker: &#039;&#039;&#039;PNP heisst &amp;quot;Pfeil Nach Platte&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
*Mit Dialekt: &#039;&#039;&#039;NPN &amp;quot;&#039;naus, Pfeil, &#039;naus&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
*..und für Gleichberechtigungsverfechter: &#039;&#039;&#039;NPN means &amp;quot;Not Pointing iN&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
JFET: [[Bild:Transistor_JFET.png]]&lt;br /&gt;
&lt;br /&gt;
MOSFET: [[Bild:Transistor_MOSFET.png]]&lt;br /&gt;
&lt;br /&gt;
* S: Source&lt;br /&gt;
* G: Gate&lt;br /&gt;
* D: Drain&lt;br /&gt;
&lt;br /&gt;
Eigentlich haben MOSFETs noch einen vierten Anschluss namens Bulk. Der ist aber nur bei Spezialtypen als Pin herausgeführt. Im Normalfall kann man ihn vergessen, da er nicht gesondert beschaltet werden muss, er ist praktisch und auch im Symbol mit Source verbunden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Die NPN/PNP Eselsbrücken funktionieren bei FETs nicht, denn bei einem P-Kanal FET zeigt der Pfeil weg vom FET!&lt;br /&gt;
Ein Bipolartransistor, im Englischen als bipolar junction transistor (BJT) bezeichnet, ist ein Transistor, bei dem Ladungsträger – negativ geladene Elektronen und positiv geladene Defektelektronen – zum Stromtransport durch den Bipolartransistor beitragen. Der BJT wird mittels eines elektrischen Stroms gesteuert und wird zum Schalten und Verstärken von Signalen ohne mechanisch bewegte Teile eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Bipolare Leistungstransistoren sind für das Schalten und Verstärken von Signalen höherer Stromstärken und Spannungen ausgelegt.&lt;br /&gt;
&lt;br /&gt;
== Typbezeichnungen == &lt;br /&gt;
&lt;br /&gt;
Neben den Typbezeichnungen wie 2Nxxxx, TIPxxx, MJxxx, MJExx gibt es noch die in Europa geläufigere  Kennzeichnung bestehend aus zwei Buchstaben und drei Ziffern. Die diversen Kennzeichnungsmöglichkeiten sind in einem eigenen Artikel ([[Kennzeichnung von Halbleitern]]) zusammengefasst.&lt;br /&gt;
&lt;br /&gt;
== Kenndaten/Parameter ==&lt;br /&gt;
&lt;br /&gt;
Im [http://www.mikrocontroller.net/topic/197676#1938546 Beitrag: Transistorparameter Erklärung] sind Links zu Erläuterungen spezieller Kürzel.&lt;br /&gt;
&lt;br /&gt;
== Transistor Grundschaltungen ==&lt;br /&gt;
&lt;br /&gt;
Generell gilt, dass Strom vom Kollektor zum Emitter nur dann fließen kann, wenn die Basis positiver (NPN) bzw. negativer (PNP) wird als der Emitter. Dabei darf die Basis nicht direkt mit Vcc (NPN) oder GND (PNP) verbunden werden, da der Basisstrom sonst zu gross wird. Es muss jeweils ein geeigneter Basiswiderstand (R_Basis) gewählt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* Artikel [[Basiswiderstand]]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0203111.htm Transistor Grundschaltungen im ElKo]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Transistor Transistor bei RoboterNetz.de]&lt;br /&gt;
&lt;br /&gt;
Es gibt drei Grundschaltungen. Der Name beschreibt den Anschluss, welcher sich auf einem festen Potential (Spannung) befindet. Die beiden anderen Anschlüsse haben bedingt durch die Schaltung ein veränderliches Potential.&lt;br /&gt;
&lt;br /&gt;
=== Kollektorschaltung (Emitterfolger)===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anwendung:&#039;&#039;&#039;&lt;br /&gt;
* Impedanzwandler&lt;br /&gt;
* Darlington-Schaltung&lt;br /&gt;
* Schalter&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Keine Phasendrehung&lt;br /&gt;
* Hohe Stromverstärkung&lt;br /&gt;
* Keine Spannungsverstärkung&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel: Transistor als Schalter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* NPN: Kollektor mit Vcc verbinden, Last an Emitter&lt;br /&gt;
* PNP: Kollektor mit GND verbinden, Last an Emitter&lt;br /&gt;
&lt;br /&gt;
In diesem Fall regelt der Transistor die Spannungen am Emitter, daher wird die Last am Emitter angeschlossen. Die Spannung am Emitter entspricht immer der an der Basis minus 0,6V, sie folgt der Basisspannung, deswegen auch der Name Emitterfolger. Daher ist diese Schaltung nicht geeignet, um 12V mit 5V zu schalten. &lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[Bild:NPN_Collector.gif | framed | center | NPN Transistor in Kollektorschaltung]] &lt;br /&gt;
|| [[Bild:PNP_Collector.gif | framed | center | PNP Transistor in Kollektorschaltung]]  &lt;br /&gt;
|}&lt;br /&gt;
* NPN: Wird &amp;lt;math&amp;gt;R_{Poti}&amp;lt;/math&amp;gt; (Spannungsteiler) erhöht, &#039;&#039;&#039;steigt&#039;&#039;&#039; die Spannung &amp;lt;math&amp;gt;U_{Last}&amp;lt;/math&amp;gt; letztlich bis auf VCC-0,6V (Basis-Emitter-Übergang).&lt;br /&gt;
* PNP: Wird &amp;lt;math&amp;gt;R_{Poti}&amp;lt;/math&amp;gt; (Spannungsteiler) erhöht, &#039;&#039;&#039;sinkt&#039;&#039;&#039; Spannung an &amp;lt;math&amp;gt;U_{Last}&amp;lt;/math&amp;gt; letztlich bis auf 0,6V (Basis-Emitter-Übergang).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0204133.htm Kollektorschaltung im ElKo]&lt;br /&gt;
&lt;br /&gt;
=== Emitterschaltung ===&lt;br /&gt;
&lt;br /&gt;
Die Emitterschaltung bietet hohe Spannungs- und Stromverstärkung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anwendung:&#039;&#039;&#039;&lt;br /&gt;
* NF- und HF-Verstärker&lt;br /&gt;
* Leistungsverstärker&lt;br /&gt;
* Transistor als Schalter&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Phasendrehung 180°&lt;br /&gt;
* Hohe Spannungsverstärkung&lt;br /&gt;
* Hohe Stromverstärkung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel: Transistor als Schalter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Last liegt am Kollektor. Der Strom durch den Schalter oder an U_Schalt steuert den Strom zwischen Kollektor und Emitter. Wird der Schalter geschlossen, fließt ein Strom.&lt;br /&gt;
&lt;br /&gt;
[[Bild:NPN_Schalter.gif | framed | center | NPN Transistor als Schalter]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                   ___                                          ___&lt;br /&gt;
Vcc/+ o-----------------------+          Vcc/+ o-----------------------+&lt;br /&gt;
                              |                                        |&lt;br /&gt;
                     ___    |&amp;lt;            U_schalt (-)       ___     |&amp;lt;  PNP             &lt;br /&gt;
             +------|___|---|  PNP             o------------|___|----|&lt;br /&gt;
             |     R_Basis  |\                             R_Basis   |\&lt;br /&gt;
    Schalter \                |                                        |&lt;br /&gt;
             |       ___      |                               ___      |&lt;br /&gt;
GND/- o------+------|___|-----+          GND/- o-------------|___|-----+&lt;br /&gt;
                   R_Last                                    R_Last     &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0204302.htm Emitterschaltung im ElKo]&lt;br /&gt;
&lt;br /&gt;
=== Basisschaltung ===&lt;br /&gt;
&lt;br /&gt;
Die Basisschaltung findet sich vor allem in Eingangsstufen in der HF-Technik. Im Schaltbetrieb wird sie praktisch nur zur [[Pegelwandler#STEP-UP: 5V -&amp;gt; 9..15V | Pegelwandlung]] für nachfolgende Stufen verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Geringe Eingangsimpedanz&lt;br /&gt;
* Keine Phasenverschiebung&lt;br /&gt;
* Hohe Bandbreite&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0205081.htm Basisschaltung im ElKo&lt;br /&gt;
&lt;br /&gt;
== FAQ aus dem Forum ==&lt;br /&gt;
&lt;br /&gt;
=== PNP/NPN als Schalter, wohin mit der Last? === &lt;br /&gt;
Für viele einfache Anwendungen kann man sich merken: &#039;&#039;&#039;Bei Schaltanwendungen darf der Basisstrom nicht durch die Last fließen&#039;&#039;&#039; (… wenn die zu schaltende Spannung höher ist als die Steuerspannung). Normalerweise kommt dabei die Emitterschaltung zum Einsatz, die Last kommt also an den Kollektor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 Ucc o─────────────┐                  Ucc o───────────┐&lt;br /&gt;
                   │                                  │&lt;br /&gt;
                  ┏┷┓               An: GND   ___   ┃&amp;lt;&lt;br /&gt;
                  ┃ ┃ R_Last             o───┤___├──┨   PNP&lt;br /&gt;
                  ┗┯┛               Aus: Ucc        ┃\&lt;br /&gt;
                   │                                  │&lt;br /&gt;
An: Ucc  ___     ┃/                                  ┏┷┓&lt;br /&gt;
    o───┤___├────┨   NPN                             ┃ ┃ R_Last&lt;br /&gt;
Aus: GND         ┃&amp;gt;                                  ┗┯┛&lt;br /&gt;
                   │                                  │&lt;br /&gt;
 GND o─────────────┘                  GND o───────────┘&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe [[Basiswiderstand]] zur Berechnung des notwendigen Basiswiderstandes bei gegebener Last R_Last für einen Transistor als Schalter.&lt;br /&gt;
&lt;br /&gt;
Siehe auch Threads im Forum: &lt;br /&gt;
* http://www.mikrocontroller.net/topic/58567 &lt;br /&gt;
* http://www.mikrocontroller.net/topic/119841&lt;br /&gt;
* oder im [http://www.elektronik-kompendium.de/sites/slt/0208031.htm Elektronik-Kompendium-Forum]&lt;br /&gt;
&lt;br /&gt;
Ist die zu schaltende Spannung &#039;&#039;&#039;genauso hoch&#039;&#039;&#039; wie die Steuerspannung darf die Last an den Emitter, und der Basiswiderstand entfällt. Der Transistor arbeitet als &#039;&#039;Impedanzwandler&#039;&#039; und steigert die Treiberfähigkeit des Mikrocontroller-Ausgangs in den Ampere-Bereich. Dieser Trick funktioniert &#039;&#039;&#039;nur&#039;&#039;&#039; mit Bipolartransistoren.&lt;br /&gt;
&lt;br /&gt;
Ist die effektive Invertierung des Schaltzustandes im oben angegebenen Bild lästig &#039;&#039;&#039;und&#039;&#039;&#039; der Strom geringer als der Ausgangsstrom, kann man den Transistor am Emitter steuern. Eine solche Schaltung funktioniert noch besser mit (Logik-Level-)MOSFETs und stellt eine einfache Kopplung zwischen Systemen mit unterschiedlicher Betriebsspannung (bspw. 1,8 V und 3,3 V) her. MOSFETs werden dazu am Source-Pin gesteuert, während das Gate an der „gegenüberliegenden“ Speisespannung „fixiert“ wird.&lt;br /&gt;
&lt;br /&gt;
=== Wie kann ich mit 5V vom Mikrocontroller 12V und mehr schalten? === &lt;br /&gt;
Schau mal hier:&lt;br /&gt;
* Wikiartikel [[Pegelwandler]]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/praxis/bausatz_pegelwandler-mit-transistoren.htm Pegelwandler im ElKo]&lt;br /&gt;
* [http://dl6gl.de/grundlagen/schalten-mit-transistoren schalten-mit-transistoren]&lt;br /&gt;
&lt;br /&gt;
oder in diesen Threads:&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/17899 Transistor als Schalter]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/14437 Vcc schalten mit MOSFET]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/29830 Schalten mit PNP-Transistor]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/104027 Transistorschalter für Versorgungsspannung]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/104951#921417 7-50V strombegrenzt schalten]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/103116#900247 P-Kanal MOSFET ansteuern]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/383039#4367479 5V/15mA mit 3,3V schalten]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/401317#4638354 High Side Treiber mit Ladungspumpe]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/347885?goto=3855574#3855574 minus -12V mit 3,3V schalten]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/516373?goto=6650986#6650986 Kurzschlußfester Schalter für VCC]&lt;br /&gt;
&lt;br /&gt;
Bei der Kollektor-Schaltung entspricht die Spannung am Emitter immer der an der Basis, daher ist sie nur bedingt geeignet. Zum Schalten können die folgenden Emitter-Schaltungen verwendet werden. Achtung: In der zweiten davon arbeitet T1 in Basisschaltung.&lt;br /&gt;
&lt;br /&gt;
Schalten gegen GND&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 +12V o------------------------+&lt;br /&gt;
                               |&lt;br /&gt;
                              .-. &lt;br /&gt;
                             ( X )  &lt;br /&gt;
                              &#039;-&#039;&lt;br /&gt;
                               |&lt;br /&gt;
                    ___      |/ T1,NPN   &lt;br /&gt;
        uC PIN o---|___|-----| BC547     &lt;br /&gt;
                   R2,4K7    |&amp;gt;&lt;br /&gt;
                               |&lt;br /&gt;
  GND o---------o--------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Schalten gegen +12V&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 +12V o--------------+----------------------+&lt;br /&gt;
                     |                      |&lt;br /&gt;
                     |   ____              |&amp;lt; T2, PNP&lt;br /&gt;
                     +--|____|----+--------|  BC557&lt;br /&gt;
                        R1,4K7    |        |\&lt;br /&gt;
                                |/T1,NPN    |&lt;br /&gt;
         Vcc/+5V o--------------| BC547     |&lt;br /&gt;
                                |&amp;gt;          |&lt;br /&gt;
                        ___       |        .-. &lt;br /&gt;
          uC PIN o-----|___|------+       ( X )  &lt;br /&gt;
                       R2,4K7              &#039;-&#039;&lt;br /&gt;
                                            |&lt;br /&gt;
  GND o----------o--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Transistor an µC ohne Vorwiderstand === &lt;br /&gt;
&lt;br /&gt;
Normalerweise sind IO Pins vom µC nicht in der Lage, große Ströme zu treiben, beim AVR maximal ~20mA. Für einen kleinen Transistor ist das immer noch zu viel und es wäre auch Stromverschwendung.&lt;br /&gt;
&lt;br /&gt;
Deshalb kann man den IO-Pin des AVRs einfach als Tristate Eingang einstellen (Portpin als Eingang und Pullup deaktivieren), damit kein Basisstrom fließt.&lt;br /&gt;
&lt;br /&gt;
Aktiviert man nun den internen Pullup-Widerstand des AVRs, agiert dieser als Basisvorwiderstand, und es fließt nur ein geringer Basisstrom (die Pullups eines AVRs liegen irgendwo bei 50k bis 100k Ohm - siehe Datenblatt).&lt;br /&gt;
&lt;br /&gt;
Nur sollte man bei kleinen Transistoren aufpassen, dass man den Portpin in der Software nie als aktiven Ausgang schaltet. &lt;br /&gt;
&lt;br /&gt;
Wenn der verwendete µC zuschaltbare Pulldown-Widerstände an seinen Pins besitzt, kann man das gleiche auch mit einem PNP-Transistor machen (natürlich nur den Pulldown aktivieren).&lt;br /&gt;
&lt;br /&gt;
Eine Anwendung wären z.&amp;amp;nbsp;B. Nixie-Röhren-Kathodentreiber (geringe Stromverstärkung nötig).&lt;br /&gt;
&lt;br /&gt;
=== Wann bipolare (NPN/PNP) und wann FETs (insbesonders, wenn LEDs im Spiel sind)?=== &lt;br /&gt;
Oft sind bipolare Transistoren (NPN/PNP) schon ausreichend, vor allem wenn &amp;quot;normale&amp;quot; LEDs (20mA) verwendet werden. FETs sind u.a. dann gut, wenn mit geringen Eingangsströmen hohe Ausgangsströme (über 300 mA) geschaltet werden sollen, also bei den Power-LEDs (Luxeon...).&lt;br /&gt;
&lt;br /&gt;
Ein Grenzfall: 500mA/5V schalten, siehe http://www.mikrocontroller.net/topic/62327.&lt;br /&gt;
&lt;br /&gt;
=== Wieso gehen bei einer Multiplex-Anzeige mit Schieberegister 74HC595 und (Darlington-)Transistor als Zeilentreiber die LED nicht ganz aus?  ===&lt;br /&gt;
Das liegt an der &#039;&#039;&#039;Miller-Kapazität&#039;&#039;&#039; des übersteuerten (Darlington-)Transistors. Der braucht erst mal einige 10µs, um zu sperren. Du mußt also erstmal beide Zeilen ausschalten, dann etwas warten und dann die nächste Zeile an. Oder Du ersetzt die Darlington durch P-FETs. [http://www.mikrocontroller.net/topic/132545]&lt;br /&gt;
&lt;br /&gt;
=== Wie steuert man ein Relais? ===&lt;br /&gt;
Normalerweise verwendet man zur Ansteuerung von Relais NPN-Transistoren in Emitterschaltung. Freilaufdiode nicht vergessen! &lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Relais mit Logik ansteuern]]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/22023/Relaisanteuerung.png Schaltbilder aus dem Forum]&lt;br /&gt;
&lt;br /&gt;
=== Was ist die Spannung &amp;lt;math&amp;gt;U_{BE\_sat}&amp;lt;/math&amp;gt; (lt. Datenblatt max. 1,2V)? ===&lt;br /&gt;
Bekanntlich verhält sich die Basis-Emitter Strecke eines Transistors wie eine Diode und &amp;lt;math&amp;gt;U_{BE\_sat}&amp;lt;/math&amp;gt; ist die bei maximal zulässigem Basisstrom anliegende Vorwärtsspannung.&lt;br /&gt;
&lt;br /&gt;
=== Was bewirkt ein Kondensator (100µF-1nF) parallel zur Basis-Emitter-Strecke nach Masse? === &lt;br /&gt;
Er wirkt mit dem Basisvorwiderstand als RC-Tiefpass. Damit wird der Transistor eigentlich nicht mehr als Schalter, sondern als Linearregler betrieben. Manche Verstärker-Schaltungen sind, gerade bei hohen Lasten, sehr schwingfreudig. Deswegen ist bei PWM so ein C nicht sinnvoll.&lt;br /&gt;
&lt;br /&gt;
=== Gibt es einen IC, der wie mehrere Transistoren funktioniert? ===&lt;br /&gt;
&lt;br /&gt;
Gibt es! Beispielsweise der &#039;&#039;&#039;ULN2803&#039;&#039;&#039; ist ein 8-fach Darlington Transistor Array mit [[Ausgangsstufen_Logik-ICs#Open_Collector|Open-Collector Ausgang]]. Damit lässt sich z.&amp;amp;nbsp;B. ein Leistungstreiber zur Ansteuerung von Schrittmotoren, Relais und anderen induktiven Lasten aufbauen.&lt;br /&gt;
&lt;br /&gt;
=== Gibt es ein Transistor-Array wie ULN28xx, das gegen Vcc schaltet? ===&lt;br /&gt;
&lt;br /&gt;
Such mal nach UDN29xx, z.&amp;amp;nbsp;B. UDN2981, UDN2987 ...&lt;br /&gt;
&lt;br /&gt;
Allegro hat zwischenzeitlich seinen Source-Treiber umbenannt. Die Bauteile heißen jetzt A2981, A2982 usw... Die Datenblätter sind identisch geblieben.&lt;br /&gt;
&lt;br /&gt;
Nachteil eines Darlington-Source-Treibers ist die hohe Sättigungsspannung (VceSat) von typisch 1,2 V. Die Eingangsspannung sollte dementsprechend höher ausgelegt werden oder gleich ein Source-Treiber mit MOSFET-Transistoren gewählt werden (z.B. Infineon ProFETs).&lt;br /&gt;
&lt;br /&gt;
=== Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein? ===&lt;br /&gt;
siehe [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
Die Summe allen Übels ist konstant. Man muss wissen, welches Bauteil sich wofür besonders eignet.&lt;br /&gt;
&lt;br /&gt;
====[[FET | MOSFET]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Bei niedrigen Spannungen &amp;lt;100V sehr gut geeignet, &amp;lt;200V gut geeignet, sehr geringe R_DS-ON Widerstände möglich (einstelliger mOhm-Bereich)&lt;br /&gt;
* Hohe Schaltgeschwindigkeiten möglich&lt;br /&gt;
* Geringe An- und Ausschaltverluste&lt;br /&gt;
* Statisch praktisch leistungslos steuerbar&lt;br /&gt;
* Bodydiode kann als Freilaufdiode in H-Brücken verwendet werden&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Drain-Source Sperrspannung zerstört das Bauteil nicht, wenn der Strom sowie die Energie begrenzt werden. (Verhalten wie [[Diode | Z-Diode]])&lt;br /&gt;
   &lt;br /&gt;
Nachteile&lt;br /&gt;
*Antiparallele Diode (Bodydiode) ist in nahezu allen MOSFETs unvermeidlich, daduch Sperren nur in einer Polarität möglich, Stromfluss über den MOSFET aber in beiden Richtungen möglich (Inversbetrieb, Synchrongleichrichter)&lt;br /&gt;
* Bei Sperrspannungen &amp;gt;100V deutlich steigende Einschaltwiderstände (R_DS-ON)&lt;br /&gt;
* Schnelles Umschalten erfordert hohe Lade-und Entladeströme ([[MOSFET-Übersicht#Mosfet-Treiber|MOSFET-Treiber]])&lt;br /&gt;
* Leitverluste quadratisch proportional zum Strom, Pv = I²*R_DS-ON&lt;br /&gt;
&lt;br /&gt;
====[[Transistor | Bipolartransistor]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Hohe Spannungsfestigkeiten möglich, bis zu 2000V&lt;br /&gt;
* Sehr hohe Schaltgeschwindigkeiten möglich&lt;br /&gt;
* Leitverluste etwa linear proportional zum Strom und Kollektor-Emitter-Sättigungsspannung, Pv = I * Uce-sat, U_CEC-sat typ 0,1...2.5 V&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Stromgesteuert, damit wird immer eine gewisse Ansteuerleistung benötigt&lt;br /&gt;
* Bei grossen Kollektorströmen nimmt die Stromverstärkung deutlich ab, dann wird ein großer Basisstrom benötigt&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Kollektor-Emitter Sperrspannung zerstört das Bauteil&lt;br /&gt;
&lt;br /&gt;
====[[IGBT]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Hohe Spannungsfestigkeiten möglich, bis zu 6600V, in üblichen Bauformen bis ca 1700V gut verfügbar&lt;br /&gt;
* Statisch praktisch leistungslos steuerbar&lt;br /&gt;
* Mit oder ohne antiparallele Diode zur Kollektor-Emitter-Strecke verfügbar&lt;br /&gt;
* Leitverluste linear proportional zum Strom und Kollektor-Emitter-Sättigungsspannung, Pv = I * Uce-sat, U_CE-sat typ. 2V&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Nur mäßige Schaltfrequenzen möglich (typ. &amp;lt;30..50kHz)&lt;br /&gt;
* Schnelles Umschalten erfordert hohe Lade-und Entladeströme ([[MOSFET-Übersicht#Mosfet-Treiber|MOSFET-Treiber]])&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Kollektor-Emitter Sperrspannung zerstört das Bauteil&lt;br /&gt;
&lt;br /&gt;
====[[TRIAC]]/Thyristor====&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Sehr hohe Spannungsfestigkeiten möglich (800V...mehrere kV, Thyristoren bis 12kV)&lt;br /&gt;
* Mit kurzen Pulsen einschaltbar, danach Selbsthaltung des Stromflusses&lt;br /&gt;
* Leitverluste linear proportional zum Strom, Pv = I * Uak, Uak typ. 0,8...1,5 V &lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Auf niedrige Schaltfrequenzen beschränkt (kHz-Bereich, Schaltzeit im Bereich von µs)&lt;br /&gt;
* Anstiegsgeschwindigkeit des Stromes muss begrenzt werden, sonst kommt es zu Bauteilschäden&lt;br /&gt;
* Anstiegsgeschwindigkeit der Spannung muss begrenzt werden, sonst kommt es zum ungewollten Zünden&lt;br /&gt;
* Stromfluss kann nicht ausgeschaltet werden, damit meist nur Einsatz an Wechselspannung&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50em&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Bauteil               &lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | optimales&amp;lt;br&amp;gt;Einsatzgebiet&lt;br /&gt;
! Kommentar&lt;br /&gt;
|-&lt;br /&gt;
| MOSFET                || 0..200V, 0..500A || im Kleinspannungsbereich meist die beste Wahl als Schalter&lt;br /&gt;
|-&lt;br /&gt;
| Bipolartransistor     || 0..1000V, 0..10A || wird mehr und mehr von MOSFETs verdrängt&lt;br /&gt;
|-&lt;br /&gt;
| IGBT                  || 200..1700V, 0..500A || optimal für hohe Spannungen und hohe Ströme&lt;br /&gt;
|-&lt;br /&gt;
| Triac/Thyristor       || 230V, 400V, 680V,&amp;lt;br&amp;gt;bis mehrere kV, 0..100A || meist für Wechselspannung, &amp;lt;br&amp;gt;Thyristoren bis 1000A im Dauerbetrieb, &amp;lt;br&amp;gt;im Pulsbetrieb einige kA. (Scheibenzelle)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Wie finde ich den richtigen Transistor für eine LED-Ansteuerung? ===&lt;br /&gt;
&lt;br /&gt;
Quelle: Beiträge [http://www.mikrocontroller.net/topic/157763#1493623] und [http://www.mikrocontroller.net/topic/157763#1494972] von yalu&lt;br /&gt;
&lt;br /&gt;
Um am Anfang wenigstens ein bisschen den Durchblick im Transistordschungel zu behalten, kannst du folgendermaßen vorgehen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nomenklatur&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nach der amerikanischen Nomenklatur beginnen die Transistornamen meist mit 2N (z.&amp;amp;nbsp;B. 2N2222 oder 2N3055) und nach der japanischen mit 2S (z.&amp;amp;nbsp;B. 2SC1815). Für den Anfang kann man sich auf europäische Transistoren beschränken, da es diese in ausreichender Auswahl gibt und die Bezeichnungen relativ gut den Transistortyp wiedergeben:&lt;br /&gt;
&lt;br /&gt;
Der erste Buchstabe bezeichnet das Halbleitermaterial (A=Germanium, B=Silizium). Germaniumtransistoren werden heute nur noch selten verwendet.&lt;br /&gt;
&lt;br /&gt;
Der zweite Buchstabe steht für den Einsatzzweck (C=Universal, D=hohe Leistung, F=Hochfrequenz, U=hohe Spannung).&lt;br /&gt;
&lt;br /&gt;
So ist also ein ACxxx ein Germaniumuniversaltransistor und ein BDxxx ein Siliziumleistungstransistor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auswahl&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn du dich für einen Grundtyp entschieden hast (für die LED ist ein BC-Typ das Richtige), gehst du auf die Webseite eines Elektronikhändlers (Reichelt, Kessler usw.), schlägst die Seite mit den BC-Transistoren auf. Da gibt es natürlich sehr viele  davon, und du brauchst jetzt eine&lt;br /&gt;
Suchreihenfolge. Als erstes Auswahlkriterium nimmst du den Preis, denn:&lt;br /&gt;
&lt;br /&gt;
* Zuviel Geld hast wahrscheinlich nicht einmal du.&lt;br /&gt;
* Billig ist meist das, was in großen Stückzahlen hergestellt wird. Was für die Masse gut ist, ist (zumindest in diesem Fall) meist auch für dich gut.&lt;br /&gt;
* Was billig und damit in Massen verkauft wird, bekommst du auch bei anderen Händlern und auch noch in 10 Jahren. Das ist wichtig, wenn deine Schaltung irgendwann einmal in Serie gefertigt werden soll.&lt;br /&gt;
&lt;br /&gt;
Gleich als nächstes überlegst du, ob du einen NPN- oder einen PNP-Typ brauchst. Das ergibt sich aus der Anordnung der Bauteile in deiner Schaltung. Hast du die Möglichkeit, die Schaltung wahlweise für einen NPN- oder einen PNP-Typ auszulegen, wählst du die Variante mit dem NPN-Typ. Um einfach eine LED über einen Mikrocontroller einzuschalten, ist i.Allg. ein NPN-Typ in Emitterschaltung richtig.&lt;br /&gt;
&lt;br /&gt;
Ein weiteres wichtiges Kriterium ist die Befestigungstechnik: Wenn dir die SMD-Löterei etwas suspekt ist, lässt du die entsprechenden Modelle erst einmal alle außen vor. Ein typisches Nicht-SMD-Gehäuse für Universaltransistoren ist TO-92. Es gibt im Internet bebilderte Listen mit den einzelnen Gehäuseformen ([[IC-Gehäuseformen#Weblinks]])&lt;br /&gt;
&lt;br /&gt;
Wenn du jetzt also bei Reichelt  die BC-Transistoren nach Preis aufsteigend sortiert hast, siehst du erst einen Schwung SMD-Tranistoren. Dann kommen ein paar Transistoren im TO-92-Gehäuse, die sind aber PNP. Etwas weiter unten kommt der erste NPN-Transistor in TO-92, nämlich der BC547C. Netterweise stehen gleich ein paar Eckdaten dabei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
BC547C  45V  0,1A  0,5W&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die 45V sind die maximale Kollektor-Emitter-Spannung, in deinem Fall also die Spannung, die du maximal schalten kannst. Da die LED bei Weitem keine 45V braucht und deine Versorgungsspannung eher in der Gegend von 5V liegt, bist du auf jeden Fall auf der sicheren Seite.&lt;br /&gt;
&lt;br /&gt;
Deine LED wird typisch mit 20mA (max. 30mA) betrieben. Der BC547C kann 100mA, also ist auch hier noch Luft.&lt;br /&gt;
&lt;br /&gt;
Zur maximalen Verlustleistung (0,5W): Wenn deine LED eingeschaltet ist, fließen bspw. 20mA. Ist der Transistor voll durchgesteuert (in Sättigung) beträgt die Kollektor-Emitter-Spannung bei diesem geringen Strom typischerweise zwischen 0,1V und 0,2V (Genaueres steht im Datenblatt). Am Transistor wird also maximal die Leistung 20mA·0,2V=4mW in Wärme umgesetzt. Bis zu 500mW dürfen es sein, also ebenfalls ok.&lt;br /&gt;
&lt;br /&gt;
Nachdem du den Transistor in engere Auswahl gezogen hast, lohnt sich auf jeden Fall ein Blick ins Datenblatt. Aus den Tabellen und Diagrammen erfährst du bspw., wie hoch der Basisstrom sein muss, um den Kollektorstrom von mindestens 20mA bei ausreichend geringer CE-Spannung bereitzustellen. Dort ist auch erklärt warum es einen BC547A, BC547B und BC547C gibt. Der letzte Buchstabe gibt nämlich die Stromverstärkungsklasse an. Da eine hohe Stromverstärkung meist wünschenswert ist und in diesem Fall keinen Aufpreis kostet, ziehst du den BC547C den anderen beiden vor.&lt;br /&gt;
&lt;br /&gt;
Da in deiner Anwendung HF- und Rauschverhalten keine Rolle spielen, bist du schon am Ziel angelangt.&lt;br /&gt;
&lt;br /&gt;
Würde deine LED 100mA statt 20mA benötigen, wären die max. 100mA des BC547 etwas knapp bemessen. Du blätterst also in der Reichelt-Liste weiter und stößt auf den BC337-40 mit 45V, 0,5A und 0,525W. Das ist genau das, wonach du suchst. Bei diesem Transistor sind die Stromverstärkungsklassen durch die Endungen -16, -25 und -40 gekennzeichnet. Es wäre ja auch&lt;br /&gt;
zu einfach, wenn immer nur A, B und C verwendet würde ;-)&lt;br /&gt;
&lt;br /&gt;
Bei Strömen ab etwa 500mA kommt man an die Grenze der Leistungsfähigkeit der BC-Typen. Dann geht es weiter mit BD. Der BD135 geht bspw. schon bis 1,5A. Das Problem bei solchen größeren Transistoren: Die Stromverstärkung ist nicht besonders hoch, so dass irgendwann der Mikrocontroller nicht mehr den benötigten Basisstrom liefern kann. Dann muss dem großen Transistor ein kleiner vorangeschaltet werden, um den erhöhten Basisstrom bereitszustellen. Man kann diese Kombination von zwei Transistoren auch fertig als Darlington-Transistor kaufen, von denen ebenfalls einige in der BD-Reihe zu finden sind (z.&amp;amp;nbsp;B. BD647). Ein Transistortyp, der sich sehr gut zum Schalten höherer Ströme eignet, ist der [[FET | MOSFET]].&lt;br /&gt;
&lt;br /&gt;
Wie schon oben angedeutet: Wenn die 30-80V die die meisten BC- und BD-Transistoren abkönnen, nicht ausreichen, suchst du weiter bei BU.&lt;br /&gt;
&lt;br /&gt;
Steigst du in die HF-Technik ein, sind BF-Transistoren eher das Richtige, wobei bei HF-Anwendungen die Auswahl der Transistoren nicht mehr das Schwierigste ist ;-)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Und wie geht&#039;s weiter?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Man könnte natürlich noch viel mehr zu diesem Thema schreiben. Ich hoffe aber, dass das Geschriebene dir wenigstens grob zeigt, wie man bei nicht allzu speziellem Anforderungen relativ schnell zu einem gewünschten Transistortyp kommt, der nicht nur die technischen Anforderungen erfüllt, sondern auch leicht beschaffbar ist.&lt;br /&gt;
&lt;br /&gt;
Werden die Anforderungen spezieller, helfen oft die Selektionstabellen auf den Webseiten der einschlägigen  Hersteller weiter. Auch Händler wie Farnell haben teilweise ganz gute Auswahlwerkzeuge. &lt;br /&gt;
&lt;br /&gt;
Wenn du dich intensiv mit Elektronik beschäftigst, wirst du wahrscheinlich noch viele  Schaltungen von Leuten zu Gesicht bekommen, die vielleicht schon etwas weiter fortgeschritten sind. Dabei wirst du immer wieder auf bestimmte Standardtypen von Transistoren (und auch anderen Bauteilen wie Operationsverstärker u.ä.) stoßen und sehen, welche [[Standardbauelemente]] &amp;quot;man&amp;quot; üblicherweise für bestimmte Anwendungen einsetzt. Mit der Zeit setzt sich dann eine Auswahl von bspw. 10 oder 20 verschiedenen Transistoren und 5 bis 10 verschiedenen OpAmps im Kopf fest, von denen man die wesentlichen Parameter auswendig kennt, so dass man ohne aufwendige Suche eine schnelle Auswahl treffen kann. Auch hier in der Artikelsammlung gibt es eine solche [[Transistor-Übersicht]].&lt;br /&gt;
&lt;br /&gt;
=== Wo ist die Antwort auf meine Frage? ===&lt;br /&gt;
&lt;br /&gt;
Vielleicht im Forum? Falls du sie da findest, dann pack das ganze doch hier rein.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/topic/165580 Gegen Vcc oder GND schalten]&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Basiswiderstand]]&lt;br /&gt;
* [[Transistor-Übersicht]]&lt;br /&gt;
* [[AVR_Transistortester]] - neu (Weiterentwickelt von Karl-Heinz Kübbeler)&lt;br /&gt;
* [[AVR-Transistortester]] - alte (von Markus Frejek)&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
== Gehäusebauformen von Transistoren ==&lt;br /&gt;
&lt;br /&gt;
=== TO-92 ===&lt;br /&gt;
Ein kleiner Aufsatz über TO-92 Transistorgehäuse und Footprints findet sich unter: [[Media:TO-92-Gehaeuse_RevB2.pdf]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://de.wikipedia.org/wiki/transistor &amp;quot;Transistor&amp;quot; bei Wikipedia]&lt;br /&gt;
* [http://www.elektronik-kompendium.de www.elektronik-kompendium.de]&lt;br /&gt;
** [http://www.elektronik-kompendium.de/sites/bau/0201291.htm Elko/Transistor]&lt;br /&gt;
* http://www.elektronikinfo.de/strom/bipolartransistoren.htm&lt;br /&gt;
* http://www.ferromel.de/tronic_1870.htm&lt;br /&gt;
* [http://www.DieElektronikerseite.de Die Elektronikerseite] Lehrgang: Der Transistor - Ein Tausendsassa&lt;br /&gt;
* [http://www.infoplease.com/encyclopedia/science/transistor-types-transistors.html Transistortypen]&lt;br /&gt;
* [http://electronics-electrical.exportersindia.com/electronic-components/transistors.htm Transistoren Industrieunternehmen Geschäftsauflistungen]&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Transistor&amp;diff=107009</id>
		<title>Transistor</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Transistor&amp;diff=107009"/>
		<updated>2024-07-11T10:06:04Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* PNP/NPN als Schalter, wohin mit der Last? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kunstwort aus &amp;quot;transfer resistor&amp;quot;, was etwa so viel bedeutet wie &amp;quot;übertragener [[Widerstand]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In den 1950-ern als praktische Anwendung des [[Halbleiter]]-Effekts erfundenes &amp;quot;solid state&amp;quot; Schalt- und Verstärkerelement, welches sehr klein ist, ohne bewegte Teile auskommt (anders als ein klassisches Relais) und keine energiefressende Heizung benötigt (anders als eine Röhre).&lt;br /&gt;
&lt;br /&gt;
Vom &amp;quot;bipolaren Transistor&amp;quot; (PNP, NPN) weiterentwickelt zum &amp;quot;Feldeffekt-Transistor&amp;quot; ([[FET]]), der heute - gefertigt mit einem preiswerten Verfahren unter Verwendung von Metall-Oxid-Schichten (MOS) - ein wesentliches Element integrierter Schaltkreise (ICs, integrated circuits) darstellt, und damit natürlich auch von [[Mikrocontroller]]n, um die es in diesem Wiki hauptsächlich geht (bzw. gehen sollte).&lt;br /&gt;
&lt;br /&gt;
== Schaltzeichen ==&lt;br /&gt;
[https://electronicsclub.info/images/transbce.gif]&lt;br /&gt;
&lt;br /&gt;
* E: Emitter&lt;br /&gt;
* B: Basis&lt;br /&gt;
* C: Collector    &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In ASCII Schaltplänen sehen Transistoren so aus:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
                       |&amp;gt;                 |/&lt;br /&gt;
NPN      |&amp;gt;   oder    -|       oder      -|&lt;br /&gt;
                       |\                 |&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                       |&amp;lt;                 |/&lt;br /&gt;
PNP:     |&amp;lt;   oder    -|       oder      -|&lt;br /&gt;
                       |\                 |&amp;lt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um zu erkennen, ob ein NPN oder PNP Transistor im Schaltplan verwendet wird, gibt es Eselsbrücken:&lt;br /&gt;
*Für Dichter: &#039;&#039;&#039;Tut der Pfeil der Basis weh, handelt&#039;s sich um PNP.&#039;&#039;&#039;&lt;br /&gt;
*Für Praktiker: &#039;&#039;&#039;PNP heisst &amp;quot;Pfeil Nach Platte&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
*Mit Dialekt: &#039;&#039;&#039;NPN &amp;quot;&#039;naus, Pfeil, &#039;naus&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
*..und für Gleichberechtigungsverfechter: &#039;&#039;&#039;NPN means &amp;quot;Not Pointing iN&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
JFET: [[Bild:Transistor_JFET.png]]&lt;br /&gt;
&lt;br /&gt;
MOSFET: [[Bild:Transistor_MOSFET.png]]&lt;br /&gt;
&lt;br /&gt;
* S: Source&lt;br /&gt;
* G: Gate&lt;br /&gt;
* D: Drain&lt;br /&gt;
&lt;br /&gt;
Eigentlich haben MOSFETs noch einen vierten Anschluss namens Bulk. Der ist aber nur bei Spezialtypen als Pin herausgeführt. Im Normalfall kann man ihn vergessen, da er nicht gesondert beschaltet werden muss, er ist praktisch und auch im Symbol mit Source verbunden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Die NPN/PNP Eselsbrücken funktionieren bei FETs nicht, denn bei einem P-Kanal FET zeigt der Pfeil weg vom FET!&lt;br /&gt;
Ein Bipolartransistor, im Englischen als bipolar junction transistor (BJT) bezeichnet, ist ein Transistor, bei dem Ladungsträger – negativ geladene Elektronen und positiv geladene Defektelektronen – zum Stromtransport durch den Bipolartransistor beitragen. Der BJT wird mittels eines elektrischen Stroms gesteuert und wird zum Schalten und Verstärken von Signalen ohne mechanisch bewegte Teile eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Bipolare Leistungstransistoren sind für das Schalten und Verstärken von Signalen höherer Stromstärken und Spannungen ausgelegt.&lt;br /&gt;
&lt;br /&gt;
== Typbezeichnungen == &lt;br /&gt;
&lt;br /&gt;
Neben den Typbezeichnungen wie 2Nxxxx, TIPxxx, MJxxx, MJExx gibt es noch die in Europa geläufigere  Kennzeichnung bestehend aus zwei Buchstaben und drei Ziffern. Die diversen Kennzeichnungsmöglichkeiten sind in einem eigenen Artikel ([[Kennzeichnung von Halbleitern]]) zusammengefasst.&lt;br /&gt;
&lt;br /&gt;
== Kenndaten/Parameter ==&lt;br /&gt;
&lt;br /&gt;
Im [http://www.mikrocontroller.net/topic/197676#1938546 Beitrag: Transistorparameter Erklärung] sind Links zu Erläuterungen spezieller Kürzel.&lt;br /&gt;
&lt;br /&gt;
== Transistor Grundschaltungen ==&lt;br /&gt;
&lt;br /&gt;
Generell gilt, dass Strom vom Kollektor zum Emitter nur dann fließen kann, wenn die Basis positiver (NPN) bzw. negativer (PNP) wird als der Emitter. Dabei darf die Basis nicht direkt mit Vcc (NPN) oder GND (PNP) verbunden werden, da der Basisstrom sonst zu gross wird. Es muss jeweils ein geeigneter Basiswiderstand (R_Basis) gewählt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* Artikel [[Basiswiderstand]]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0203111.htm Transistor Grundschaltungen im ElKo]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Transistor Transistor bei RoboterNetz.de]&lt;br /&gt;
&lt;br /&gt;
Es gibt drei Grundschaltungen. Der Name beschreibt den Anschluss, welcher sich auf einem festen Potential (Spannung) befindet. Die beiden anderen Anschlüsse haben bedingt durch die Schaltung ein veränderliches Potential.&lt;br /&gt;
&lt;br /&gt;
=== Kollektorschaltung (Emitterfolger)===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anwendung:&#039;&#039;&#039;&lt;br /&gt;
* Impedanzwandler&lt;br /&gt;
* Darlington-Schaltung&lt;br /&gt;
* Schalter&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Keine Phasendrehung&lt;br /&gt;
* Hohe Stromverstärkung&lt;br /&gt;
* Keine Spannungsverstärkung&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel: Transistor als Schalter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* NPN: Kollektor mit Vcc verbinden, Last an Emitter&lt;br /&gt;
* PNP: Kollektor mit GND verbinden, Last an Emitter&lt;br /&gt;
&lt;br /&gt;
In diesem Fall regelt der Transistor die Spannungen am Emitter, daher wird die Last am Emitter angeschlossen. Die Spannung am Emitter entspricht immer der an der Basis minus 0,6V, sie folgt der Basisspannung, deswegen auch der Name Emitterfolger. Daher ist diese Schaltung nicht geeignet, um 12V mit 5V zu schalten. &lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[Bild:NPN_Collector.gif | framed | center | NPN Transistor in Kollektorschaltung]] &lt;br /&gt;
|| [[Bild:PNP_Collector.gif | framed | center | PNP Transistor in Kollektorschaltung]]  &lt;br /&gt;
|}&lt;br /&gt;
* NPN: Wird &amp;lt;math&amp;gt;R_{Poti}&amp;lt;/math&amp;gt; (Spannungsteiler) erhöht, &#039;&#039;&#039;steigt&#039;&#039;&#039; die Spannung &amp;lt;math&amp;gt;U_{Last}&amp;lt;/math&amp;gt; letztlich bis auf VCC-0,6V (Basis-Emitter-Übergang).&lt;br /&gt;
* PNP: Wird &amp;lt;math&amp;gt;R_{Poti}&amp;lt;/math&amp;gt; (Spannungsteiler) erhöht, &#039;&#039;&#039;sinkt&#039;&#039;&#039; Spannung an &amp;lt;math&amp;gt;U_{Last}&amp;lt;/math&amp;gt; letztlich bis auf 0,6V (Basis-Emitter-Übergang).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0204133.htm Kollektorschaltung im ElKo]&lt;br /&gt;
&lt;br /&gt;
=== Emitterschaltung ===&lt;br /&gt;
&lt;br /&gt;
Die Emitterschaltung bietet hohe Spannungs- und Stromverstärkung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anwendung:&#039;&#039;&#039;&lt;br /&gt;
* NF- und HF-Verstärker&lt;br /&gt;
* Leistungsverstärker&lt;br /&gt;
* Transistor als Schalter&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Phasendrehung 180°&lt;br /&gt;
* Hohe Spannungsverstärkung&lt;br /&gt;
* Hohe Stromverstärkung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel: Transistor als Schalter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Last liegt am Kollektor. Der Strom durch den Schalter oder an U_Schalt steuert den Strom zwischen Kollektor und Emitter. Wird der Schalter geschlossen, fließt ein Strom.&lt;br /&gt;
&lt;br /&gt;
[[Bild:NPN_Schalter.gif | framed | center | NPN Transistor als Schalter]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                   ___                                          ___&lt;br /&gt;
Vcc/+ o-----------------------+          Vcc/+ o-----------------------+&lt;br /&gt;
                              |                                        |&lt;br /&gt;
                     ___    |&amp;lt;            U_schalt (-)       ___     |&amp;lt;  PNP             &lt;br /&gt;
             +------|___|---|  PNP             o------------|___|----|&lt;br /&gt;
             |     R_Basis  |\                             R_Basis   |\&lt;br /&gt;
    Schalter \                |                                        |&lt;br /&gt;
             |       ___      |                               ___      |&lt;br /&gt;
GND/- o------+------|___|-----+          GND/- o-------------|___|-----+&lt;br /&gt;
                   R_Last                                    R_Last     &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0204302.htm Emitterschaltung im ElKo]&lt;br /&gt;
&lt;br /&gt;
=== Basisschaltung ===&lt;br /&gt;
&lt;br /&gt;
Die Basisschaltung findet sich vor allem in Eingangsstufen in der HF-Technik. Im Schaltbetrieb wird sie praktisch nur zur [[Pegelwandler#STEP-UP: 5V -&amp;gt; 9..15V | Pegelwandlung]] für nachfolgende Stufen verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Geringe Eingangsimpedanz&lt;br /&gt;
* Keine Phasenverschiebung&lt;br /&gt;
* Hohe Bandbreite&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0205081.htm Basisschaltung im ElKo&lt;br /&gt;
&lt;br /&gt;
== FAQ aus dem Forum ==&lt;br /&gt;
&lt;br /&gt;
=== PNP/NPN als Schalter, wohin mit der Last? === &lt;br /&gt;
Für viele einfache Anwendungen kann man sich merken: &#039;&#039;&#039;Bei Schaltanwendungen darf der Basisstrom nicht durch die Last fließen&#039;&#039;&#039; (… wenn die zu schaltende Spannung höher ist als die Steuerspannung). Normalerweise kommt dabei die Emitterschaltung zum Einsatz, die Last kommt also an den Kollektor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 Vcc o─────────────┐                  Vcc o───────────┐&lt;br /&gt;
                   │                                  │&lt;br /&gt;
                  ┏┷┓               An: GND   ___   ┃&amp;lt;&lt;br /&gt;
                  ┃ ┃ R_Last             o───┤___├──┨   PNP&lt;br /&gt;
                  ┗┯┛               Aus: Vcc        ┃\&lt;br /&gt;
                   │                                  │&lt;br /&gt;
An: Vcc  ___     ┃/                                  ┏┷┓&lt;br /&gt;
    o───┤___├────┨   NPN                             ┃ ┃ R_Last&lt;br /&gt;
Aus: GND         ┃&amp;gt;                                  ┗┯┛&lt;br /&gt;
                   │                                  │&lt;br /&gt;
 GND o─────────────┘                  GND o───────────┘&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe [[Basiswiderstand]] zur Berechnung des notwendigen Basiswiderstandes bei gegebener Last R_Last für einen Transistor als Schalter.&lt;br /&gt;
&lt;br /&gt;
Siehe auch Threads im Forum: &lt;br /&gt;
* http://www.mikrocontroller.net/topic/58567 &lt;br /&gt;
* http://www.mikrocontroller.net/topic/119841&lt;br /&gt;
* oder im [http://www.elektronik-kompendium.de/sites/slt/0208031.htm Elektronik-Kompendium-Forum]&lt;br /&gt;
&lt;br /&gt;
Ist die zu schaltende Spannung &#039;&#039;&#039;genauso hoch&#039;&#039;&#039; wie die Steuerspannung darf die Last an den Emitter, und der Basiswiderstand entfällt. Der Transistor arbeitet als &#039;&#039;Impedanzwandler&#039;&#039; und steigert die Treiberfähigkeit des Mikrocontroller-Ausgangs in den Ampere-Bereich. Dieser Trick funktioniert &#039;&#039;&#039;nur&#039;&#039;&#039; mit Bipolartransistoren.&lt;br /&gt;
&lt;br /&gt;
Ist die effektive Invertierung des Schaltzustandes im oben angegebenen Bild lästig &#039;&#039;&#039;und&#039;&#039;&#039; der Strom geringer als der Ausgangsstrom, kann man den Transistor am Emitter steuern. Eine solche Schaltung funktioniert noch besser mit (Logik-Level-)MOSFETs und stellt eine einfache Kopplung zwischen Systemen mit unterschiedlicher Betriebsspannung (bspw. 1,8 V und 3,3 V) her. MOSFETs werden dazu am Source-Pin gesteuert, während das Gate an der „gegenüberliegenden“ Speisespannung „fixiert“ wird.&lt;br /&gt;
&lt;br /&gt;
=== Wie kann ich mit 5V vom Mikrocontroller 12V und mehr schalten? === &lt;br /&gt;
Schau mal hier:&lt;br /&gt;
* Wikiartikel [[Pegelwandler]]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/praxis/bausatz_pegelwandler-mit-transistoren.htm Pegelwandler im ElKo]&lt;br /&gt;
* [http://dl6gl.de/grundlagen/schalten-mit-transistoren schalten-mit-transistoren]&lt;br /&gt;
&lt;br /&gt;
oder in diesen Threads:&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/17899 Transistor als Schalter]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/14437 Vcc schalten mit MOSFET]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/29830 Schalten mit PNP-Transistor]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/104027 Transistorschalter für Versorgungsspannung]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/104951#921417 7-50V strombegrenzt schalten]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/103116#900247 P-Kanal MOSFET ansteuern]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/383039#4367479 5V/15mA mit 3,3V schalten]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/401317#4638354 High Side Treiber mit Ladungspumpe]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/347885?goto=3855574#3855574 minus -12V mit 3,3V schalten]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/516373?goto=6650986#6650986 Kurzschlußfester Schalter für VCC]&lt;br /&gt;
&lt;br /&gt;
Bei der Kollektor-Schaltung entspricht die Spannung am Emitter immer der an der Basis, daher ist sie nur bedingt geeignet. Zum Schalten können die folgenden Emitter-Schaltungen verwendet werden. Achtung: In der zweiten davon arbeitet T1 in Basisschaltung.&lt;br /&gt;
&lt;br /&gt;
Schalten gegen GND&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 +12V o------------------------+&lt;br /&gt;
                               |&lt;br /&gt;
                              .-. &lt;br /&gt;
                             ( X )  &lt;br /&gt;
                              &#039;-&#039;&lt;br /&gt;
                               |&lt;br /&gt;
                    ___      |/ T1,NPN   &lt;br /&gt;
        uC PIN o---|___|-----| BC547     &lt;br /&gt;
                   R2,4K7    |&amp;gt;&lt;br /&gt;
                               |&lt;br /&gt;
  GND o---------o--------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Schalten gegen +12V&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 +12V o--------------+----------------------+&lt;br /&gt;
                     |                      |&lt;br /&gt;
                     |   ____              |&amp;lt; T2, PNP&lt;br /&gt;
                     +--|____|----+--------|  BC557&lt;br /&gt;
                        R1,4K7    |        |\&lt;br /&gt;
                                |/T1,NPN    |&lt;br /&gt;
         Vcc/+5V o--------------| BC547     |&lt;br /&gt;
                                |&amp;gt;          |&lt;br /&gt;
                        ___       |        .-. &lt;br /&gt;
          uC PIN o-----|___|------+       ( X )  &lt;br /&gt;
                       R2,4K7              &#039;-&#039;&lt;br /&gt;
                                            |&lt;br /&gt;
  GND o----------o--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Transistor an µC ohne Vorwiderstand === &lt;br /&gt;
&lt;br /&gt;
Normalerweise sind IO Pins vom µC nicht in der Lage, große Ströme zu treiben, beim AVR maximal ~20mA. Für einen kleinen Transistor ist das immer noch zu viel und es wäre auch Stromverschwendung.&lt;br /&gt;
&lt;br /&gt;
Deshalb kann man den IO-Pin des AVRs einfach als Tristate Eingang einstellen (Portpin als Eingang und Pullup deaktivieren), damit kein Basisstrom fließt.&lt;br /&gt;
&lt;br /&gt;
Aktiviert man nun den internen Pullup-Widerstand des AVRs, agiert dieser als Basisvorwiderstand, und es fließt nur ein geringer Basisstrom (die Pullups eines AVRs liegen irgendwo bei 50k bis 100k Ohm - siehe Datenblatt).&lt;br /&gt;
&lt;br /&gt;
Nur sollte man bei kleinen Transistoren aufpassen, dass man den Portpin in der Software nie als aktiven Ausgang schaltet. &lt;br /&gt;
&lt;br /&gt;
Wenn der verwendete µC zuschaltbare Pulldown-Widerstände an seinen Pins besitzt, kann man das gleiche auch mit einem PNP-Transistor machen (natürlich nur den Pulldown aktivieren).&lt;br /&gt;
&lt;br /&gt;
Eine Anwendung wären z.&amp;amp;nbsp;B. Nixie-Röhren-Kathodentreiber (geringe Stromverstärkung nötig).&lt;br /&gt;
&lt;br /&gt;
=== Wann bipolare (NPN/PNP) und wann FETs (insbesonders, wenn LEDs im Spiel sind)?=== &lt;br /&gt;
Oft sind bipolare Transistoren (NPN/PNP) schon ausreichend, vor allem wenn &amp;quot;normale&amp;quot; LEDs (20mA) verwendet werden. FETs sind u.a. dann gut, wenn mit geringen Eingangsströmen hohe Ausgangsströme (über 300 mA) geschaltet werden sollen, also bei den Power-LEDs (Luxeon...).&lt;br /&gt;
&lt;br /&gt;
Ein Grenzfall: 500mA/5V schalten, siehe http://www.mikrocontroller.net/topic/62327.&lt;br /&gt;
&lt;br /&gt;
=== Wieso gehen bei einer Multiplex-Anzeige mit Schieberegister 74HC595 und (Darlington-)Transistor als Zeilentreiber die LED nicht ganz aus?  ===&lt;br /&gt;
Das liegt an der &#039;&#039;&#039;Miller-Kapazität&#039;&#039;&#039; des übersteuerten (Darlington-)Transistors. Der braucht erst mal einige 10µs, um zu sperren. Du mußt also erstmal beide Zeilen ausschalten, dann etwas warten und dann die nächste Zeile an. Oder Du ersetzt die Darlington durch P-FETs. [http://www.mikrocontroller.net/topic/132545]&lt;br /&gt;
&lt;br /&gt;
=== Wie steuert man ein Relais? ===&lt;br /&gt;
Normalerweise verwendet man zur Ansteuerung von Relais NPN-Transistoren in Emitterschaltung. Freilaufdiode nicht vergessen! &lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Relais mit Logik ansteuern]]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/22023/Relaisanteuerung.png Schaltbilder aus dem Forum]&lt;br /&gt;
&lt;br /&gt;
=== Was ist die Spannung &amp;lt;math&amp;gt;U_{BE\_sat}&amp;lt;/math&amp;gt; (lt. Datenblatt max. 1,2V)? ===&lt;br /&gt;
Bekanntlich verhält sich die Basis-Emitter Strecke eines Transistors wie eine Diode und &amp;lt;math&amp;gt;U_{BE\_sat}&amp;lt;/math&amp;gt; ist die bei maximal zulässigem Basisstrom anliegende Vorwärtsspannung.&lt;br /&gt;
&lt;br /&gt;
=== Was bewirkt ein Kondensator (100µF-1nF) parallel zur Basis-Emitter-Strecke nach Masse? === &lt;br /&gt;
Er wirkt mit dem Basisvorwiderstand als RC-Tiefpass. Damit wird der Transistor eigentlich nicht mehr als Schalter, sondern als Linearregler betrieben. Manche Verstärker-Schaltungen sind, gerade bei hohen Lasten, sehr schwingfreudig. Deswegen ist bei PWM so ein C nicht sinnvoll.&lt;br /&gt;
&lt;br /&gt;
=== Gibt es einen IC, der wie mehrere Transistoren funktioniert? ===&lt;br /&gt;
&lt;br /&gt;
Gibt es! Beispielsweise der &#039;&#039;&#039;ULN2803&#039;&#039;&#039; ist ein 8-fach Darlington Transistor Array mit [[Ausgangsstufen_Logik-ICs#Open_Collector|Open-Collector Ausgang]]. Damit lässt sich z.&amp;amp;nbsp;B. ein Leistungstreiber zur Ansteuerung von Schrittmotoren, Relais und anderen induktiven Lasten aufbauen.&lt;br /&gt;
&lt;br /&gt;
=== Gibt es ein Transistor-Array wie ULN28xx, das gegen Vcc schaltet? ===&lt;br /&gt;
&lt;br /&gt;
Such mal nach UDN29xx, z.&amp;amp;nbsp;B. UDN2981, UDN2987 ...&lt;br /&gt;
&lt;br /&gt;
Allegro hat zwischenzeitlich seinen Source-Treiber umbenannt. Die Bauteile heißen jetzt A2981, A2982 usw... Die Datenblätter sind identisch geblieben.&lt;br /&gt;
&lt;br /&gt;
Nachteil eines Darlington-Source-Treibers ist die hohe Sättigungsspannung (VceSat) von typisch 1,2 V. Die Eingangsspannung sollte dementsprechend höher ausgelegt werden oder gleich ein Source-Treiber mit MOSFET-Transistoren gewählt werden (z.B. Infineon ProFETs).&lt;br /&gt;
&lt;br /&gt;
=== Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein? ===&lt;br /&gt;
siehe [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
Die Summe allen Übels ist konstant. Man muss wissen, welches Bauteil sich wofür besonders eignet.&lt;br /&gt;
&lt;br /&gt;
====[[FET | MOSFET]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Bei niedrigen Spannungen &amp;lt;100V sehr gut geeignet, &amp;lt;200V gut geeignet, sehr geringe R_DS-ON Widerstände möglich (einstelliger mOhm-Bereich)&lt;br /&gt;
* Hohe Schaltgeschwindigkeiten möglich&lt;br /&gt;
* Geringe An- und Ausschaltverluste&lt;br /&gt;
* Statisch praktisch leistungslos steuerbar&lt;br /&gt;
* Bodydiode kann als Freilaufdiode in H-Brücken verwendet werden&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Drain-Source Sperrspannung zerstört das Bauteil nicht, wenn der Strom sowie die Energie begrenzt werden. (Verhalten wie [[Diode | Z-Diode]])&lt;br /&gt;
   &lt;br /&gt;
Nachteile&lt;br /&gt;
*Antiparallele Diode (Bodydiode) ist in nahezu allen MOSFETs unvermeidlich, daduch Sperren nur in einer Polarität möglich, Stromfluss über den MOSFET aber in beiden Richtungen möglich (Inversbetrieb, Synchrongleichrichter)&lt;br /&gt;
* Bei Sperrspannungen &amp;gt;100V deutlich steigende Einschaltwiderstände (R_DS-ON)&lt;br /&gt;
* Schnelles Umschalten erfordert hohe Lade-und Entladeströme ([[MOSFET-Übersicht#Mosfet-Treiber|MOSFET-Treiber]])&lt;br /&gt;
* Leitverluste quadratisch proportional zum Strom, Pv = I²*R_DS-ON&lt;br /&gt;
&lt;br /&gt;
====[[Transistor | Bipolartransistor]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Hohe Spannungsfestigkeiten möglich, bis zu 2000V&lt;br /&gt;
* Sehr hohe Schaltgeschwindigkeiten möglich&lt;br /&gt;
* Leitverluste etwa linear proportional zum Strom und Kollektor-Emitter-Sättigungsspannung, Pv = I * Uce-sat, U_CEC-sat typ 0,1...2.5 V&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Stromgesteuert, damit wird immer eine gewisse Ansteuerleistung benötigt&lt;br /&gt;
* Bei grossen Kollektorströmen nimmt die Stromverstärkung deutlich ab, dann wird ein großer Basisstrom benötigt&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Kollektor-Emitter Sperrspannung zerstört das Bauteil&lt;br /&gt;
&lt;br /&gt;
====[[IGBT]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Hohe Spannungsfestigkeiten möglich, bis zu 6600V, in üblichen Bauformen bis ca 1700V gut verfügbar&lt;br /&gt;
* Statisch praktisch leistungslos steuerbar&lt;br /&gt;
* Mit oder ohne antiparallele Diode zur Kollektor-Emitter-Strecke verfügbar&lt;br /&gt;
* Leitverluste linear proportional zum Strom und Kollektor-Emitter-Sättigungsspannung, Pv = I * Uce-sat, U_CE-sat typ. 2V&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Nur mäßige Schaltfrequenzen möglich (typ. &amp;lt;30..50kHz)&lt;br /&gt;
* Schnelles Umschalten erfordert hohe Lade-und Entladeströme ([[MOSFET-Übersicht#Mosfet-Treiber|MOSFET-Treiber]])&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Kollektor-Emitter Sperrspannung zerstört das Bauteil&lt;br /&gt;
&lt;br /&gt;
====[[TRIAC]]/Thyristor====&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Sehr hohe Spannungsfestigkeiten möglich (800V...mehrere kV, Thyristoren bis 12kV)&lt;br /&gt;
* Mit kurzen Pulsen einschaltbar, danach Selbsthaltung des Stromflusses&lt;br /&gt;
* Leitverluste linear proportional zum Strom, Pv = I * Uak, Uak typ. 0,8...1,5 V &lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Auf niedrige Schaltfrequenzen beschränkt (kHz-Bereich, Schaltzeit im Bereich von µs)&lt;br /&gt;
* Anstiegsgeschwindigkeit des Stromes muss begrenzt werden, sonst kommt es zu Bauteilschäden&lt;br /&gt;
* Anstiegsgeschwindigkeit der Spannung muss begrenzt werden, sonst kommt es zum ungewollten Zünden&lt;br /&gt;
* Stromfluss kann nicht ausgeschaltet werden, damit meist nur Einsatz an Wechselspannung&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50em&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Bauteil               &lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | optimales&amp;lt;br&amp;gt;Einsatzgebiet&lt;br /&gt;
! Kommentar&lt;br /&gt;
|-&lt;br /&gt;
| MOSFET                || 0..200V, 0..500A || im Kleinspannungsbereich meist die beste Wahl als Schalter&lt;br /&gt;
|-&lt;br /&gt;
| Bipolartransistor     || 0..1000V, 0..10A || wird mehr und mehr von MOSFETs verdrängt&lt;br /&gt;
|-&lt;br /&gt;
| IGBT                  || 200..1700V, 0..500A || optimal für hohe Spannungen und hohe Ströme&lt;br /&gt;
|-&lt;br /&gt;
| Triac/Thyristor       || 230V, 400V, 680V,&amp;lt;br&amp;gt;bis mehrere kV, 0..100A || meist für Wechselspannung, &amp;lt;br&amp;gt;Thyristoren bis 1000A im Dauerbetrieb, &amp;lt;br&amp;gt;im Pulsbetrieb einige kA. (Scheibenzelle)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Wie finde ich den richtigen Transistor für eine LED-Ansteuerung? ===&lt;br /&gt;
&lt;br /&gt;
Quelle: Beiträge [http://www.mikrocontroller.net/topic/157763#1493623] und [http://www.mikrocontroller.net/topic/157763#1494972] von yalu&lt;br /&gt;
&lt;br /&gt;
Um am Anfang wenigstens ein bisschen den Durchblick im Transistordschungel zu behalten, kannst du folgendermaßen vorgehen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nomenklatur&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nach der amerikanischen Nomenklatur beginnen die Transistornamen meist mit 2N (z.&amp;amp;nbsp;B. 2N2222 oder 2N3055) und nach der japanischen mit 2S (z.&amp;amp;nbsp;B. 2SC1815). Für den Anfang kann man sich auf europäische Transistoren beschränken, da es diese in ausreichender Auswahl gibt und die Bezeichnungen relativ gut den Transistortyp wiedergeben:&lt;br /&gt;
&lt;br /&gt;
Der erste Buchstabe bezeichnet das Halbleitermaterial (A=Germanium, B=Silizium). Germaniumtransistoren werden heute nur noch selten verwendet.&lt;br /&gt;
&lt;br /&gt;
Der zweite Buchstabe steht für den Einsatzzweck (C=Universal, D=hohe Leistung, F=Hochfrequenz, U=hohe Spannung).&lt;br /&gt;
&lt;br /&gt;
So ist also ein ACxxx ein Germaniumuniversaltransistor und ein BDxxx ein Siliziumleistungstransistor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auswahl&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn du dich für einen Grundtyp entschieden hast (für die LED ist ein BC-Typ das Richtige), gehst du auf die Webseite eines Elektronikhändlers (Reichelt, Kessler usw.), schlägst die Seite mit den BC-Transistoren auf. Da gibt es natürlich sehr viele  davon, und du brauchst jetzt eine&lt;br /&gt;
Suchreihenfolge. Als erstes Auswahlkriterium nimmst du den Preis, denn:&lt;br /&gt;
&lt;br /&gt;
* Zuviel Geld hast wahrscheinlich nicht einmal du.&lt;br /&gt;
* Billig ist meist das, was in großen Stückzahlen hergestellt wird. Was für die Masse gut ist, ist (zumindest in diesem Fall) meist auch für dich gut.&lt;br /&gt;
* Was billig und damit in Massen verkauft wird, bekommst du auch bei anderen Händlern und auch noch in 10 Jahren. Das ist wichtig, wenn deine Schaltung irgendwann einmal in Serie gefertigt werden soll.&lt;br /&gt;
&lt;br /&gt;
Gleich als nächstes überlegst du, ob du einen NPN- oder einen PNP-Typ brauchst. Das ergibt sich aus der Anordnung der Bauteile in deiner Schaltung. Hast du die Möglichkeit, die Schaltung wahlweise für einen NPN- oder einen PNP-Typ auszulegen, wählst du die Variante mit dem NPN-Typ. Um einfach eine LED über einen Mikrocontroller einzuschalten, ist i.Allg. ein NPN-Typ in Emitterschaltung richtig.&lt;br /&gt;
&lt;br /&gt;
Ein weiteres wichtiges Kriterium ist die Befestigungstechnik: Wenn dir die SMD-Löterei etwas suspekt ist, lässt du die entsprechenden Modelle erst einmal alle außen vor. Ein typisches Nicht-SMD-Gehäuse für Universaltransistoren ist TO-92. Es gibt im Internet bebilderte Listen mit den einzelnen Gehäuseformen ([[IC-Gehäuseformen#Weblinks]])&lt;br /&gt;
&lt;br /&gt;
Wenn du jetzt also bei Reichelt  die BC-Transistoren nach Preis aufsteigend sortiert hast, siehst du erst einen Schwung SMD-Tranistoren. Dann kommen ein paar Transistoren im TO-92-Gehäuse, die sind aber PNP. Etwas weiter unten kommt der erste NPN-Transistor in TO-92, nämlich der BC547C. Netterweise stehen gleich ein paar Eckdaten dabei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
BC547C  45V  0,1A  0,5W&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die 45V sind die maximale Kollektor-Emitter-Spannung, in deinem Fall also die Spannung, die du maximal schalten kannst. Da die LED bei Weitem keine 45V braucht und deine Versorgungsspannung eher in der Gegend von 5V liegt, bist du auf jeden Fall auf der sicheren Seite.&lt;br /&gt;
&lt;br /&gt;
Deine LED wird typisch mit 20mA (max. 30mA) betrieben. Der BC547C kann 100mA, also ist auch hier noch Luft.&lt;br /&gt;
&lt;br /&gt;
Zur maximalen Verlustleistung (0,5W): Wenn deine LED eingeschaltet ist, fließen bspw. 20mA. Ist der Transistor voll durchgesteuert (in Sättigung) beträgt die Kollektor-Emitter-Spannung bei diesem geringen Strom typischerweise zwischen 0,1V und 0,2V (Genaueres steht im Datenblatt). Am Transistor wird also maximal die Leistung 20mA·0,2V=4mW in Wärme umgesetzt. Bis zu 500mW dürfen es sein, also ebenfalls ok.&lt;br /&gt;
&lt;br /&gt;
Nachdem du den Transistor in engere Auswahl gezogen hast, lohnt sich auf jeden Fall ein Blick ins Datenblatt. Aus den Tabellen und Diagrammen erfährst du bspw., wie hoch der Basisstrom sein muss, um den Kollektorstrom von mindestens 20mA bei ausreichend geringer CE-Spannung bereitzustellen. Dort ist auch erklärt warum es einen BC547A, BC547B und BC547C gibt. Der letzte Buchstabe gibt nämlich die Stromverstärkungsklasse an. Da eine hohe Stromverstärkung meist wünschenswert ist und in diesem Fall keinen Aufpreis kostet, ziehst du den BC547C den anderen beiden vor.&lt;br /&gt;
&lt;br /&gt;
Da in deiner Anwendung HF- und Rauschverhalten keine Rolle spielen, bist du schon am Ziel angelangt.&lt;br /&gt;
&lt;br /&gt;
Würde deine LED 100mA statt 20mA benötigen, wären die max. 100mA des BC547 etwas knapp bemessen. Du blätterst also in der Reichelt-Liste weiter und stößt auf den BC337-40 mit 45V, 0,5A und 0,525W. Das ist genau das, wonach du suchst. Bei diesem Transistor sind die Stromverstärkungsklassen durch die Endungen -16, -25 und -40 gekennzeichnet. Es wäre ja auch&lt;br /&gt;
zu einfach, wenn immer nur A, B und C verwendet würde ;-)&lt;br /&gt;
&lt;br /&gt;
Bei Strömen ab etwa 500mA kommt man an die Grenze der Leistungsfähigkeit der BC-Typen. Dann geht es weiter mit BD. Der BD135 geht bspw. schon bis 1,5A. Das Problem bei solchen größeren Transistoren: Die Stromverstärkung ist nicht besonders hoch, so dass irgendwann der Mikrocontroller nicht mehr den benötigten Basisstrom liefern kann. Dann muss dem großen Transistor ein kleiner vorangeschaltet werden, um den erhöhten Basisstrom bereitszustellen. Man kann diese Kombination von zwei Transistoren auch fertig als Darlington-Transistor kaufen, von denen ebenfalls einige in der BD-Reihe zu finden sind (z.&amp;amp;nbsp;B. BD647). Ein Transistortyp, der sich sehr gut zum Schalten höherer Ströme eignet, ist der [[FET | MOSFET]].&lt;br /&gt;
&lt;br /&gt;
Wie schon oben angedeutet: Wenn die 30-80V die die meisten BC- und BD-Transistoren abkönnen, nicht ausreichen, suchst du weiter bei BU.&lt;br /&gt;
&lt;br /&gt;
Steigst du in die HF-Technik ein, sind BF-Transistoren eher das Richtige, wobei bei HF-Anwendungen die Auswahl der Transistoren nicht mehr das Schwierigste ist ;-)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Und wie geht&#039;s weiter?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Man könnte natürlich noch viel mehr zu diesem Thema schreiben. Ich hoffe aber, dass das Geschriebene dir wenigstens grob zeigt, wie man bei nicht allzu speziellem Anforderungen relativ schnell zu einem gewünschten Transistortyp kommt, der nicht nur die technischen Anforderungen erfüllt, sondern auch leicht beschaffbar ist.&lt;br /&gt;
&lt;br /&gt;
Werden die Anforderungen spezieller, helfen oft die Selektionstabellen auf den Webseiten der einschlägigen  Hersteller weiter. Auch Händler wie Farnell haben teilweise ganz gute Auswahlwerkzeuge. &lt;br /&gt;
&lt;br /&gt;
Wenn du dich intensiv mit Elektronik beschäftigst, wirst du wahrscheinlich noch viele  Schaltungen von Leuten zu Gesicht bekommen, die vielleicht schon etwas weiter fortgeschritten sind. Dabei wirst du immer wieder auf bestimmte Standardtypen von Transistoren (und auch anderen Bauteilen wie Operationsverstärker u.ä.) stoßen und sehen, welche [[Standardbauelemente]] &amp;quot;man&amp;quot; üblicherweise für bestimmte Anwendungen einsetzt. Mit der Zeit setzt sich dann eine Auswahl von bspw. 10 oder 20 verschiedenen Transistoren und 5 bis 10 verschiedenen OpAmps im Kopf fest, von denen man die wesentlichen Parameter auswendig kennt, so dass man ohne aufwendige Suche eine schnelle Auswahl treffen kann. Auch hier in der Artikelsammlung gibt es eine solche [[Transistor-Übersicht]].&lt;br /&gt;
&lt;br /&gt;
=== Wo ist die Antwort auf meine Frage? ===&lt;br /&gt;
&lt;br /&gt;
Vielleicht im Forum? Falls du sie da findest, dann pack das ganze doch hier rein.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/topic/165580 Gegen Vcc oder GND schalten]&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Basiswiderstand]]&lt;br /&gt;
* [[Transistor-Übersicht]]&lt;br /&gt;
* [[AVR_Transistortester]] - neu (Weiterentwickelt von Karl-Heinz Kübbeler)&lt;br /&gt;
* [[AVR-Transistortester]] - alte (von Markus Frejek)&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
== Gehäusebauformen von Transistoren ==&lt;br /&gt;
&lt;br /&gt;
=== TO-92 ===&lt;br /&gt;
Ein kleiner Aufsatz über TO-92 Transistorgehäuse und Footprints findet sich unter: [[Media:TO-92-Gehaeuse_RevB2.pdf]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://de.wikipedia.org/wiki/transistor &amp;quot;Transistor&amp;quot; bei Wikipedia]&lt;br /&gt;
* [http://www.elektronik-kompendium.de www.elektronik-kompendium.de]&lt;br /&gt;
** [http://www.elektronik-kompendium.de/sites/bau/0201291.htm Elko/Transistor]&lt;br /&gt;
* http://www.elektronikinfo.de/strom/bipolartransistoren.htm&lt;br /&gt;
* http://www.ferromel.de/tronic_1870.htm&lt;br /&gt;
* [http://www.DieElektronikerseite.de Die Elektronikerseite] Lehrgang: Der Transistor - Ein Tausendsassa&lt;br /&gt;
* [http://www.infoplease.com/encyclopedia/science/transistor-types-transistors.html Transistortypen]&lt;br /&gt;
* [http://electronics-electrical.exportersindia.com/electronic-components/transistors.htm Transistoren Industrieunternehmen Geschäftsauflistungen]&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Transistor&amp;diff=107008</id>
		<title>Transistor</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Transistor&amp;diff=107008"/>
		<updated>2024-07-11T10:05:44Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* PNP/NPN als Schalter, wohin mit der Last? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kunstwort aus &amp;quot;transfer resistor&amp;quot;, was etwa so viel bedeutet wie &amp;quot;übertragener [[Widerstand]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In den 1950-ern als praktische Anwendung des [[Halbleiter]]-Effekts erfundenes &amp;quot;solid state&amp;quot; Schalt- und Verstärkerelement, welches sehr klein ist, ohne bewegte Teile auskommt (anders als ein klassisches Relais) und keine energiefressende Heizung benötigt (anders als eine Röhre).&lt;br /&gt;
&lt;br /&gt;
Vom &amp;quot;bipolaren Transistor&amp;quot; (PNP, NPN) weiterentwickelt zum &amp;quot;Feldeffekt-Transistor&amp;quot; ([[FET]]), der heute - gefertigt mit einem preiswerten Verfahren unter Verwendung von Metall-Oxid-Schichten (MOS) - ein wesentliches Element integrierter Schaltkreise (ICs, integrated circuits) darstellt, und damit natürlich auch von [[Mikrocontroller]]n, um die es in diesem Wiki hauptsächlich geht (bzw. gehen sollte).&lt;br /&gt;
&lt;br /&gt;
== Schaltzeichen ==&lt;br /&gt;
[https://electronicsclub.info/images/transbce.gif]&lt;br /&gt;
&lt;br /&gt;
* E: Emitter&lt;br /&gt;
* B: Basis&lt;br /&gt;
* C: Collector    &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In ASCII Schaltplänen sehen Transistoren so aus:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
                       |&amp;gt;                 |/&lt;br /&gt;
NPN      |&amp;gt;   oder    -|       oder      -|&lt;br /&gt;
                       |\                 |&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                       |&amp;lt;                 |/&lt;br /&gt;
PNP:     |&amp;lt;   oder    -|       oder      -|&lt;br /&gt;
                       |\                 |&amp;lt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um zu erkennen, ob ein NPN oder PNP Transistor im Schaltplan verwendet wird, gibt es Eselsbrücken:&lt;br /&gt;
*Für Dichter: &#039;&#039;&#039;Tut der Pfeil der Basis weh, handelt&#039;s sich um PNP.&#039;&#039;&#039;&lt;br /&gt;
*Für Praktiker: &#039;&#039;&#039;PNP heisst &amp;quot;Pfeil Nach Platte&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
*Mit Dialekt: &#039;&#039;&#039;NPN &amp;quot;&#039;naus, Pfeil, &#039;naus&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
*..und für Gleichberechtigungsverfechter: &#039;&#039;&#039;NPN means &amp;quot;Not Pointing iN&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
JFET: [[Bild:Transistor_JFET.png]]&lt;br /&gt;
&lt;br /&gt;
MOSFET: [[Bild:Transistor_MOSFET.png]]&lt;br /&gt;
&lt;br /&gt;
* S: Source&lt;br /&gt;
* G: Gate&lt;br /&gt;
* D: Drain&lt;br /&gt;
&lt;br /&gt;
Eigentlich haben MOSFETs noch einen vierten Anschluss namens Bulk. Der ist aber nur bei Spezialtypen als Pin herausgeführt. Im Normalfall kann man ihn vergessen, da er nicht gesondert beschaltet werden muss, er ist praktisch und auch im Symbol mit Source verbunden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Die NPN/PNP Eselsbrücken funktionieren bei FETs nicht, denn bei einem P-Kanal FET zeigt der Pfeil weg vom FET!&lt;br /&gt;
Ein Bipolartransistor, im Englischen als bipolar junction transistor (BJT) bezeichnet, ist ein Transistor, bei dem Ladungsträger – negativ geladene Elektronen und positiv geladene Defektelektronen – zum Stromtransport durch den Bipolartransistor beitragen. Der BJT wird mittels eines elektrischen Stroms gesteuert und wird zum Schalten und Verstärken von Signalen ohne mechanisch bewegte Teile eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Bipolare Leistungstransistoren sind für das Schalten und Verstärken von Signalen höherer Stromstärken und Spannungen ausgelegt.&lt;br /&gt;
&lt;br /&gt;
== Typbezeichnungen == &lt;br /&gt;
&lt;br /&gt;
Neben den Typbezeichnungen wie 2Nxxxx, TIPxxx, MJxxx, MJExx gibt es noch die in Europa geläufigere  Kennzeichnung bestehend aus zwei Buchstaben und drei Ziffern. Die diversen Kennzeichnungsmöglichkeiten sind in einem eigenen Artikel ([[Kennzeichnung von Halbleitern]]) zusammengefasst.&lt;br /&gt;
&lt;br /&gt;
== Kenndaten/Parameter ==&lt;br /&gt;
&lt;br /&gt;
Im [http://www.mikrocontroller.net/topic/197676#1938546 Beitrag: Transistorparameter Erklärung] sind Links zu Erläuterungen spezieller Kürzel.&lt;br /&gt;
&lt;br /&gt;
== Transistor Grundschaltungen ==&lt;br /&gt;
&lt;br /&gt;
Generell gilt, dass Strom vom Kollektor zum Emitter nur dann fließen kann, wenn die Basis positiver (NPN) bzw. negativer (PNP) wird als der Emitter. Dabei darf die Basis nicht direkt mit Vcc (NPN) oder GND (PNP) verbunden werden, da der Basisstrom sonst zu gross wird. Es muss jeweils ein geeigneter Basiswiderstand (R_Basis) gewählt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* Artikel [[Basiswiderstand]]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0203111.htm Transistor Grundschaltungen im ElKo]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Transistor Transistor bei RoboterNetz.de]&lt;br /&gt;
&lt;br /&gt;
Es gibt drei Grundschaltungen. Der Name beschreibt den Anschluss, welcher sich auf einem festen Potential (Spannung) befindet. Die beiden anderen Anschlüsse haben bedingt durch die Schaltung ein veränderliches Potential.&lt;br /&gt;
&lt;br /&gt;
=== Kollektorschaltung (Emitterfolger)===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anwendung:&#039;&#039;&#039;&lt;br /&gt;
* Impedanzwandler&lt;br /&gt;
* Darlington-Schaltung&lt;br /&gt;
* Schalter&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Keine Phasendrehung&lt;br /&gt;
* Hohe Stromverstärkung&lt;br /&gt;
* Keine Spannungsverstärkung&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel: Transistor als Schalter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* NPN: Kollektor mit Vcc verbinden, Last an Emitter&lt;br /&gt;
* PNP: Kollektor mit GND verbinden, Last an Emitter&lt;br /&gt;
&lt;br /&gt;
In diesem Fall regelt der Transistor die Spannungen am Emitter, daher wird die Last am Emitter angeschlossen. Die Spannung am Emitter entspricht immer der an der Basis minus 0,6V, sie folgt der Basisspannung, deswegen auch der Name Emitterfolger. Daher ist diese Schaltung nicht geeignet, um 12V mit 5V zu schalten. &lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[Bild:NPN_Collector.gif | framed | center | NPN Transistor in Kollektorschaltung]] &lt;br /&gt;
|| [[Bild:PNP_Collector.gif | framed | center | PNP Transistor in Kollektorschaltung]]  &lt;br /&gt;
|}&lt;br /&gt;
* NPN: Wird &amp;lt;math&amp;gt;R_{Poti}&amp;lt;/math&amp;gt; (Spannungsteiler) erhöht, &#039;&#039;&#039;steigt&#039;&#039;&#039; die Spannung &amp;lt;math&amp;gt;U_{Last}&amp;lt;/math&amp;gt; letztlich bis auf VCC-0,6V (Basis-Emitter-Übergang).&lt;br /&gt;
* PNP: Wird &amp;lt;math&amp;gt;R_{Poti}&amp;lt;/math&amp;gt; (Spannungsteiler) erhöht, &#039;&#039;&#039;sinkt&#039;&#039;&#039; Spannung an &amp;lt;math&amp;gt;U_{Last}&amp;lt;/math&amp;gt; letztlich bis auf 0,6V (Basis-Emitter-Übergang).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0204133.htm Kollektorschaltung im ElKo]&lt;br /&gt;
&lt;br /&gt;
=== Emitterschaltung ===&lt;br /&gt;
&lt;br /&gt;
Die Emitterschaltung bietet hohe Spannungs- und Stromverstärkung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anwendung:&#039;&#039;&#039;&lt;br /&gt;
* NF- und HF-Verstärker&lt;br /&gt;
* Leistungsverstärker&lt;br /&gt;
* Transistor als Schalter&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Phasendrehung 180°&lt;br /&gt;
* Hohe Spannungsverstärkung&lt;br /&gt;
* Hohe Stromverstärkung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel: Transistor als Schalter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Last liegt am Kollektor. Der Strom durch den Schalter oder an U_Schalt steuert den Strom zwischen Kollektor und Emitter. Wird der Schalter geschlossen, fließt ein Strom.&lt;br /&gt;
&lt;br /&gt;
[[Bild:NPN_Schalter.gif | framed | center | NPN Transistor als Schalter]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                   ___                                          ___&lt;br /&gt;
Vcc/+ o-----------------------+          Vcc/+ o-----------------------+&lt;br /&gt;
                              |                                        |&lt;br /&gt;
                     ___    |&amp;lt;            U_schalt (-)       ___     |&amp;lt;  PNP             &lt;br /&gt;
             +------|___|---|  PNP             o------------|___|----|&lt;br /&gt;
             |     R_Basis  |\                             R_Basis   |\&lt;br /&gt;
    Schalter \                |                                        |&lt;br /&gt;
             |       ___      |                               ___      |&lt;br /&gt;
GND/- o------+------|___|-----+          GND/- o-------------|___|-----+&lt;br /&gt;
                   R_Last                                    R_Last     &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0204302.htm Emitterschaltung im ElKo]&lt;br /&gt;
&lt;br /&gt;
=== Basisschaltung ===&lt;br /&gt;
&lt;br /&gt;
Die Basisschaltung findet sich vor allem in Eingangsstufen in der HF-Technik. Im Schaltbetrieb wird sie praktisch nur zur [[Pegelwandler#STEP-UP: 5V -&amp;gt; 9..15V | Pegelwandlung]] für nachfolgende Stufen verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Geringe Eingangsimpedanz&lt;br /&gt;
* Keine Phasenverschiebung&lt;br /&gt;
* Hohe Bandbreite&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0205081.htm Basisschaltung im ElKo&lt;br /&gt;
&lt;br /&gt;
== FAQ aus dem Forum ==&lt;br /&gt;
&lt;br /&gt;
=== PNP/NPN als Schalter, wohin mit der Last? === &lt;br /&gt;
Für viele einfache Anwendungen kann man sich merken: &#039;&#039;&#039;Bei Schaltanwendungen darf der Basisstrom nicht durch die Last fließen&#039;&#039;&#039; (… wenn die zu schaltende Spannung höher ist als die Steuerspannung). Normalerweise kommt dabei die Emitterschaltung zum Einsatz, die Last kommt also an den Kollektor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;─│┌┐└┘├┤┬┴ ━┃┏┓┗┛┷┯&lt;br /&gt;
 Vcc o─────────────┐                  Vcc o───────────┐&lt;br /&gt;
                   │                                  │&lt;br /&gt;
                  ┏┷┓               An: GND   ___   ┃&amp;lt;&lt;br /&gt;
                  ┃ ┃ R_Last             o───┤___├──┨   PNP&lt;br /&gt;
                  ┗┯┛               Aus: Vcc        ┃\&lt;br /&gt;
                   │                                  │&lt;br /&gt;
An: Vcc  ___     ┃/                                  ┏┷┓&lt;br /&gt;
    o───┤___├────┨   NPN                             ┃ ┃ R_Last&lt;br /&gt;
Aus: GND         ┃&amp;gt;                                  ┗┯┛&lt;br /&gt;
                   │                                  │&lt;br /&gt;
 GND o─────────────┘                  GND o───────────┘&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe [[Basiswiderstand]] zur Berechnung des notwendigen Basiswiderstandes bei gegebener Last R_Last für einen Transistor als Schalter.&lt;br /&gt;
&lt;br /&gt;
Siehe auch Threads im Forum: &lt;br /&gt;
* http://www.mikrocontroller.net/topic/58567 &lt;br /&gt;
* http://www.mikrocontroller.net/topic/119841&lt;br /&gt;
* oder im [http://www.elektronik-kompendium.de/sites/slt/0208031.htm Elektronik-Kompendium-Forum]&lt;br /&gt;
&lt;br /&gt;
Ist die zu schaltende Spannung &#039;&#039;&#039;genauso hoch&#039;&#039;&#039; wie die Steuerspannung darf die Last an den Emitter, und der Basiswiderstand entfällt. Der Transistor arbeitet als &#039;&#039;Impedanzwandler&#039;&#039; und steigert die Treiberfähigkeit des Mikrocontroller-Ausgangs in den Ampere-Bereich. Dieser Trick funktioniert &#039;&#039;&#039;nur&#039;&#039;&#039; mit Bipolartransistoren.&lt;br /&gt;
&lt;br /&gt;
Ist die effektive Invertierung des Schaltzustandes im oben angegebenen Bild lästig &#039;&#039;&#039;und&#039;&#039;&#039; der Strom geringer als der Ausgangsstrom, kann man den Transistor am Emitter steuern. Eine solche Schaltung funktioniert noch besser mit (Logik-Level-)MOSFETs und stellt eine einfache Kopplung zwischen Systemen mit unterschiedlicher Betriebsspannung (bspw. 1,8 V und 3,3 V) her. MOSFETs werden dazu am Source-Pin gesteuert, während das Gate an der „gegenüberliegenden“ Speisespannung „fixiert“ wird.&lt;br /&gt;
&lt;br /&gt;
=== Wie kann ich mit 5V vom Mikrocontroller 12V und mehr schalten? === &lt;br /&gt;
Schau mal hier:&lt;br /&gt;
* Wikiartikel [[Pegelwandler]]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/praxis/bausatz_pegelwandler-mit-transistoren.htm Pegelwandler im ElKo]&lt;br /&gt;
* [http://dl6gl.de/grundlagen/schalten-mit-transistoren schalten-mit-transistoren]&lt;br /&gt;
&lt;br /&gt;
oder in diesen Threads:&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/17899 Transistor als Schalter]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/14437 Vcc schalten mit MOSFET]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/29830 Schalten mit PNP-Transistor]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/104027 Transistorschalter für Versorgungsspannung]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/104951#921417 7-50V strombegrenzt schalten]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/103116#900247 P-Kanal MOSFET ansteuern]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/383039#4367479 5V/15mA mit 3,3V schalten]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/401317#4638354 High Side Treiber mit Ladungspumpe]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/347885?goto=3855574#3855574 minus -12V mit 3,3V schalten]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/516373?goto=6650986#6650986 Kurzschlußfester Schalter für VCC]&lt;br /&gt;
&lt;br /&gt;
Bei der Kollektor-Schaltung entspricht die Spannung am Emitter immer der an der Basis, daher ist sie nur bedingt geeignet. Zum Schalten können die folgenden Emitter-Schaltungen verwendet werden. Achtung: In der zweiten davon arbeitet T1 in Basisschaltung.&lt;br /&gt;
&lt;br /&gt;
Schalten gegen GND&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 +12V o------------------------+&lt;br /&gt;
                               |&lt;br /&gt;
                              .-. &lt;br /&gt;
                             ( X )  &lt;br /&gt;
                              &#039;-&#039;&lt;br /&gt;
                               |&lt;br /&gt;
                    ___      |/ T1,NPN   &lt;br /&gt;
        uC PIN o---|___|-----| BC547     &lt;br /&gt;
                   R2,4K7    |&amp;gt;&lt;br /&gt;
                               |&lt;br /&gt;
  GND o---------o--------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Schalten gegen +12V&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 +12V o--------------+----------------------+&lt;br /&gt;
                     |                      |&lt;br /&gt;
                     |   ____              |&amp;lt; T2, PNP&lt;br /&gt;
                     +--|____|----+--------|  BC557&lt;br /&gt;
                        R1,4K7    |        |\&lt;br /&gt;
                                |/T1,NPN    |&lt;br /&gt;
         Vcc/+5V o--------------| BC547     |&lt;br /&gt;
                                |&amp;gt;          |&lt;br /&gt;
                        ___       |        .-. &lt;br /&gt;
          uC PIN o-----|___|------+       ( X )  &lt;br /&gt;
                       R2,4K7              &#039;-&#039;&lt;br /&gt;
                                            |&lt;br /&gt;
  GND o----------o--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Transistor an µC ohne Vorwiderstand === &lt;br /&gt;
&lt;br /&gt;
Normalerweise sind IO Pins vom µC nicht in der Lage, große Ströme zu treiben, beim AVR maximal ~20mA. Für einen kleinen Transistor ist das immer noch zu viel und es wäre auch Stromverschwendung.&lt;br /&gt;
&lt;br /&gt;
Deshalb kann man den IO-Pin des AVRs einfach als Tristate Eingang einstellen (Portpin als Eingang und Pullup deaktivieren), damit kein Basisstrom fließt.&lt;br /&gt;
&lt;br /&gt;
Aktiviert man nun den internen Pullup-Widerstand des AVRs, agiert dieser als Basisvorwiderstand, und es fließt nur ein geringer Basisstrom (die Pullups eines AVRs liegen irgendwo bei 50k bis 100k Ohm - siehe Datenblatt).&lt;br /&gt;
&lt;br /&gt;
Nur sollte man bei kleinen Transistoren aufpassen, dass man den Portpin in der Software nie als aktiven Ausgang schaltet. &lt;br /&gt;
&lt;br /&gt;
Wenn der verwendete µC zuschaltbare Pulldown-Widerstände an seinen Pins besitzt, kann man das gleiche auch mit einem PNP-Transistor machen (natürlich nur den Pulldown aktivieren).&lt;br /&gt;
&lt;br /&gt;
Eine Anwendung wären z.&amp;amp;nbsp;B. Nixie-Röhren-Kathodentreiber (geringe Stromverstärkung nötig).&lt;br /&gt;
&lt;br /&gt;
=== Wann bipolare (NPN/PNP) und wann FETs (insbesonders, wenn LEDs im Spiel sind)?=== &lt;br /&gt;
Oft sind bipolare Transistoren (NPN/PNP) schon ausreichend, vor allem wenn &amp;quot;normale&amp;quot; LEDs (20mA) verwendet werden. FETs sind u.a. dann gut, wenn mit geringen Eingangsströmen hohe Ausgangsströme (über 300 mA) geschaltet werden sollen, also bei den Power-LEDs (Luxeon...).&lt;br /&gt;
&lt;br /&gt;
Ein Grenzfall: 500mA/5V schalten, siehe http://www.mikrocontroller.net/topic/62327.&lt;br /&gt;
&lt;br /&gt;
=== Wieso gehen bei einer Multiplex-Anzeige mit Schieberegister 74HC595 und (Darlington-)Transistor als Zeilentreiber die LED nicht ganz aus?  ===&lt;br /&gt;
Das liegt an der &#039;&#039;&#039;Miller-Kapazität&#039;&#039;&#039; des übersteuerten (Darlington-)Transistors. Der braucht erst mal einige 10µs, um zu sperren. Du mußt also erstmal beide Zeilen ausschalten, dann etwas warten und dann die nächste Zeile an. Oder Du ersetzt die Darlington durch P-FETs. [http://www.mikrocontroller.net/topic/132545]&lt;br /&gt;
&lt;br /&gt;
=== Wie steuert man ein Relais? ===&lt;br /&gt;
Normalerweise verwendet man zur Ansteuerung von Relais NPN-Transistoren in Emitterschaltung. Freilaufdiode nicht vergessen! &lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Relais mit Logik ansteuern]]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/22023/Relaisanteuerung.png Schaltbilder aus dem Forum]&lt;br /&gt;
&lt;br /&gt;
=== Was ist die Spannung &amp;lt;math&amp;gt;U_{BE\_sat}&amp;lt;/math&amp;gt; (lt. Datenblatt max. 1,2V)? ===&lt;br /&gt;
Bekanntlich verhält sich die Basis-Emitter Strecke eines Transistors wie eine Diode und &amp;lt;math&amp;gt;U_{BE\_sat}&amp;lt;/math&amp;gt; ist die bei maximal zulässigem Basisstrom anliegende Vorwärtsspannung.&lt;br /&gt;
&lt;br /&gt;
=== Was bewirkt ein Kondensator (100µF-1nF) parallel zur Basis-Emitter-Strecke nach Masse? === &lt;br /&gt;
Er wirkt mit dem Basisvorwiderstand als RC-Tiefpass. Damit wird der Transistor eigentlich nicht mehr als Schalter, sondern als Linearregler betrieben. Manche Verstärker-Schaltungen sind, gerade bei hohen Lasten, sehr schwingfreudig. Deswegen ist bei PWM so ein C nicht sinnvoll.&lt;br /&gt;
&lt;br /&gt;
=== Gibt es einen IC, der wie mehrere Transistoren funktioniert? ===&lt;br /&gt;
&lt;br /&gt;
Gibt es! Beispielsweise der &#039;&#039;&#039;ULN2803&#039;&#039;&#039; ist ein 8-fach Darlington Transistor Array mit [[Ausgangsstufen_Logik-ICs#Open_Collector|Open-Collector Ausgang]]. Damit lässt sich z.&amp;amp;nbsp;B. ein Leistungstreiber zur Ansteuerung von Schrittmotoren, Relais und anderen induktiven Lasten aufbauen.&lt;br /&gt;
&lt;br /&gt;
=== Gibt es ein Transistor-Array wie ULN28xx, das gegen Vcc schaltet? ===&lt;br /&gt;
&lt;br /&gt;
Such mal nach UDN29xx, z.&amp;amp;nbsp;B. UDN2981, UDN2987 ...&lt;br /&gt;
&lt;br /&gt;
Allegro hat zwischenzeitlich seinen Source-Treiber umbenannt. Die Bauteile heißen jetzt A2981, A2982 usw... Die Datenblätter sind identisch geblieben.&lt;br /&gt;
&lt;br /&gt;
Nachteil eines Darlington-Source-Treibers ist die hohe Sättigungsspannung (VceSat) von typisch 1,2 V. Die Eingangsspannung sollte dementsprechend höher ausgelegt werden oder gleich ein Source-Treiber mit MOSFET-Transistoren gewählt werden (z.B. Infineon ProFETs).&lt;br /&gt;
&lt;br /&gt;
=== Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein? ===&lt;br /&gt;
siehe [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
Die Summe allen Übels ist konstant. Man muss wissen, welches Bauteil sich wofür besonders eignet.&lt;br /&gt;
&lt;br /&gt;
====[[FET | MOSFET]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Bei niedrigen Spannungen &amp;lt;100V sehr gut geeignet, &amp;lt;200V gut geeignet, sehr geringe R_DS-ON Widerstände möglich (einstelliger mOhm-Bereich)&lt;br /&gt;
* Hohe Schaltgeschwindigkeiten möglich&lt;br /&gt;
* Geringe An- und Ausschaltverluste&lt;br /&gt;
* Statisch praktisch leistungslos steuerbar&lt;br /&gt;
* Bodydiode kann als Freilaufdiode in H-Brücken verwendet werden&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Drain-Source Sperrspannung zerstört das Bauteil nicht, wenn der Strom sowie die Energie begrenzt werden. (Verhalten wie [[Diode | Z-Diode]])&lt;br /&gt;
   &lt;br /&gt;
Nachteile&lt;br /&gt;
*Antiparallele Diode (Bodydiode) ist in nahezu allen MOSFETs unvermeidlich, daduch Sperren nur in einer Polarität möglich, Stromfluss über den MOSFET aber in beiden Richtungen möglich (Inversbetrieb, Synchrongleichrichter)&lt;br /&gt;
* Bei Sperrspannungen &amp;gt;100V deutlich steigende Einschaltwiderstände (R_DS-ON)&lt;br /&gt;
* Schnelles Umschalten erfordert hohe Lade-und Entladeströme ([[MOSFET-Übersicht#Mosfet-Treiber|MOSFET-Treiber]])&lt;br /&gt;
* Leitverluste quadratisch proportional zum Strom, Pv = I²*R_DS-ON&lt;br /&gt;
&lt;br /&gt;
====[[Transistor | Bipolartransistor]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Hohe Spannungsfestigkeiten möglich, bis zu 2000V&lt;br /&gt;
* Sehr hohe Schaltgeschwindigkeiten möglich&lt;br /&gt;
* Leitverluste etwa linear proportional zum Strom und Kollektor-Emitter-Sättigungsspannung, Pv = I * Uce-sat, U_CEC-sat typ 0,1...2.5 V&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Stromgesteuert, damit wird immer eine gewisse Ansteuerleistung benötigt&lt;br /&gt;
* Bei grossen Kollektorströmen nimmt die Stromverstärkung deutlich ab, dann wird ein großer Basisstrom benötigt&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Kollektor-Emitter Sperrspannung zerstört das Bauteil&lt;br /&gt;
&lt;br /&gt;
====[[IGBT]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Hohe Spannungsfestigkeiten möglich, bis zu 6600V, in üblichen Bauformen bis ca 1700V gut verfügbar&lt;br /&gt;
* Statisch praktisch leistungslos steuerbar&lt;br /&gt;
* Mit oder ohne antiparallele Diode zur Kollektor-Emitter-Strecke verfügbar&lt;br /&gt;
* Leitverluste linear proportional zum Strom und Kollektor-Emitter-Sättigungsspannung, Pv = I * Uce-sat, U_CE-sat typ. 2V&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Nur mäßige Schaltfrequenzen möglich (typ. &amp;lt;30..50kHz)&lt;br /&gt;
* Schnelles Umschalten erfordert hohe Lade-und Entladeströme ([[MOSFET-Übersicht#Mosfet-Treiber|MOSFET-Treiber]])&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Kollektor-Emitter Sperrspannung zerstört das Bauteil&lt;br /&gt;
&lt;br /&gt;
====[[TRIAC]]/Thyristor====&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Sehr hohe Spannungsfestigkeiten möglich (800V...mehrere kV, Thyristoren bis 12kV)&lt;br /&gt;
* Mit kurzen Pulsen einschaltbar, danach Selbsthaltung des Stromflusses&lt;br /&gt;
* Leitverluste linear proportional zum Strom, Pv = I * Uak, Uak typ. 0,8...1,5 V &lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Auf niedrige Schaltfrequenzen beschränkt (kHz-Bereich, Schaltzeit im Bereich von µs)&lt;br /&gt;
* Anstiegsgeschwindigkeit des Stromes muss begrenzt werden, sonst kommt es zu Bauteilschäden&lt;br /&gt;
* Anstiegsgeschwindigkeit der Spannung muss begrenzt werden, sonst kommt es zum ungewollten Zünden&lt;br /&gt;
* Stromfluss kann nicht ausgeschaltet werden, damit meist nur Einsatz an Wechselspannung&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50em&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Bauteil               &lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | optimales&amp;lt;br&amp;gt;Einsatzgebiet&lt;br /&gt;
! Kommentar&lt;br /&gt;
|-&lt;br /&gt;
| MOSFET                || 0..200V, 0..500A || im Kleinspannungsbereich meist die beste Wahl als Schalter&lt;br /&gt;
|-&lt;br /&gt;
| Bipolartransistor     || 0..1000V, 0..10A || wird mehr und mehr von MOSFETs verdrängt&lt;br /&gt;
|-&lt;br /&gt;
| IGBT                  || 200..1700V, 0..500A || optimal für hohe Spannungen und hohe Ströme&lt;br /&gt;
|-&lt;br /&gt;
| Triac/Thyristor       || 230V, 400V, 680V,&amp;lt;br&amp;gt;bis mehrere kV, 0..100A || meist für Wechselspannung, &amp;lt;br&amp;gt;Thyristoren bis 1000A im Dauerbetrieb, &amp;lt;br&amp;gt;im Pulsbetrieb einige kA. (Scheibenzelle)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Wie finde ich den richtigen Transistor für eine LED-Ansteuerung? ===&lt;br /&gt;
&lt;br /&gt;
Quelle: Beiträge [http://www.mikrocontroller.net/topic/157763#1493623] und [http://www.mikrocontroller.net/topic/157763#1494972] von yalu&lt;br /&gt;
&lt;br /&gt;
Um am Anfang wenigstens ein bisschen den Durchblick im Transistordschungel zu behalten, kannst du folgendermaßen vorgehen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nomenklatur&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nach der amerikanischen Nomenklatur beginnen die Transistornamen meist mit 2N (z.&amp;amp;nbsp;B. 2N2222 oder 2N3055) und nach der japanischen mit 2S (z.&amp;amp;nbsp;B. 2SC1815). Für den Anfang kann man sich auf europäische Transistoren beschränken, da es diese in ausreichender Auswahl gibt und die Bezeichnungen relativ gut den Transistortyp wiedergeben:&lt;br /&gt;
&lt;br /&gt;
Der erste Buchstabe bezeichnet das Halbleitermaterial (A=Germanium, B=Silizium). Germaniumtransistoren werden heute nur noch selten verwendet.&lt;br /&gt;
&lt;br /&gt;
Der zweite Buchstabe steht für den Einsatzzweck (C=Universal, D=hohe Leistung, F=Hochfrequenz, U=hohe Spannung).&lt;br /&gt;
&lt;br /&gt;
So ist also ein ACxxx ein Germaniumuniversaltransistor und ein BDxxx ein Siliziumleistungstransistor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auswahl&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn du dich für einen Grundtyp entschieden hast (für die LED ist ein BC-Typ das Richtige), gehst du auf die Webseite eines Elektronikhändlers (Reichelt, Kessler usw.), schlägst die Seite mit den BC-Transistoren auf. Da gibt es natürlich sehr viele  davon, und du brauchst jetzt eine&lt;br /&gt;
Suchreihenfolge. Als erstes Auswahlkriterium nimmst du den Preis, denn:&lt;br /&gt;
&lt;br /&gt;
* Zuviel Geld hast wahrscheinlich nicht einmal du.&lt;br /&gt;
* Billig ist meist das, was in großen Stückzahlen hergestellt wird. Was für die Masse gut ist, ist (zumindest in diesem Fall) meist auch für dich gut.&lt;br /&gt;
* Was billig und damit in Massen verkauft wird, bekommst du auch bei anderen Händlern und auch noch in 10 Jahren. Das ist wichtig, wenn deine Schaltung irgendwann einmal in Serie gefertigt werden soll.&lt;br /&gt;
&lt;br /&gt;
Gleich als nächstes überlegst du, ob du einen NPN- oder einen PNP-Typ brauchst. Das ergibt sich aus der Anordnung der Bauteile in deiner Schaltung. Hast du die Möglichkeit, die Schaltung wahlweise für einen NPN- oder einen PNP-Typ auszulegen, wählst du die Variante mit dem NPN-Typ. Um einfach eine LED über einen Mikrocontroller einzuschalten, ist i.Allg. ein NPN-Typ in Emitterschaltung richtig.&lt;br /&gt;
&lt;br /&gt;
Ein weiteres wichtiges Kriterium ist die Befestigungstechnik: Wenn dir die SMD-Löterei etwas suspekt ist, lässt du die entsprechenden Modelle erst einmal alle außen vor. Ein typisches Nicht-SMD-Gehäuse für Universaltransistoren ist TO-92. Es gibt im Internet bebilderte Listen mit den einzelnen Gehäuseformen ([[IC-Gehäuseformen#Weblinks]])&lt;br /&gt;
&lt;br /&gt;
Wenn du jetzt also bei Reichelt  die BC-Transistoren nach Preis aufsteigend sortiert hast, siehst du erst einen Schwung SMD-Tranistoren. Dann kommen ein paar Transistoren im TO-92-Gehäuse, die sind aber PNP. Etwas weiter unten kommt der erste NPN-Transistor in TO-92, nämlich der BC547C. Netterweise stehen gleich ein paar Eckdaten dabei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
BC547C  45V  0,1A  0,5W&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die 45V sind die maximale Kollektor-Emitter-Spannung, in deinem Fall also die Spannung, die du maximal schalten kannst. Da die LED bei Weitem keine 45V braucht und deine Versorgungsspannung eher in der Gegend von 5V liegt, bist du auf jeden Fall auf der sicheren Seite.&lt;br /&gt;
&lt;br /&gt;
Deine LED wird typisch mit 20mA (max. 30mA) betrieben. Der BC547C kann 100mA, also ist auch hier noch Luft.&lt;br /&gt;
&lt;br /&gt;
Zur maximalen Verlustleistung (0,5W): Wenn deine LED eingeschaltet ist, fließen bspw. 20mA. Ist der Transistor voll durchgesteuert (in Sättigung) beträgt die Kollektor-Emitter-Spannung bei diesem geringen Strom typischerweise zwischen 0,1V und 0,2V (Genaueres steht im Datenblatt). Am Transistor wird also maximal die Leistung 20mA·0,2V=4mW in Wärme umgesetzt. Bis zu 500mW dürfen es sein, also ebenfalls ok.&lt;br /&gt;
&lt;br /&gt;
Nachdem du den Transistor in engere Auswahl gezogen hast, lohnt sich auf jeden Fall ein Blick ins Datenblatt. Aus den Tabellen und Diagrammen erfährst du bspw., wie hoch der Basisstrom sein muss, um den Kollektorstrom von mindestens 20mA bei ausreichend geringer CE-Spannung bereitzustellen. Dort ist auch erklärt warum es einen BC547A, BC547B und BC547C gibt. Der letzte Buchstabe gibt nämlich die Stromverstärkungsklasse an. Da eine hohe Stromverstärkung meist wünschenswert ist und in diesem Fall keinen Aufpreis kostet, ziehst du den BC547C den anderen beiden vor.&lt;br /&gt;
&lt;br /&gt;
Da in deiner Anwendung HF- und Rauschverhalten keine Rolle spielen, bist du schon am Ziel angelangt.&lt;br /&gt;
&lt;br /&gt;
Würde deine LED 100mA statt 20mA benötigen, wären die max. 100mA des BC547 etwas knapp bemessen. Du blätterst also in der Reichelt-Liste weiter und stößt auf den BC337-40 mit 45V, 0,5A und 0,525W. Das ist genau das, wonach du suchst. Bei diesem Transistor sind die Stromverstärkungsklassen durch die Endungen -16, -25 und -40 gekennzeichnet. Es wäre ja auch&lt;br /&gt;
zu einfach, wenn immer nur A, B und C verwendet würde ;-)&lt;br /&gt;
&lt;br /&gt;
Bei Strömen ab etwa 500mA kommt man an die Grenze der Leistungsfähigkeit der BC-Typen. Dann geht es weiter mit BD. Der BD135 geht bspw. schon bis 1,5A. Das Problem bei solchen größeren Transistoren: Die Stromverstärkung ist nicht besonders hoch, so dass irgendwann der Mikrocontroller nicht mehr den benötigten Basisstrom liefern kann. Dann muss dem großen Transistor ein kleiner vorangeschaltet werden, um den erhöhten Basisstrom bereitszustellen. Man kann diese Kombination von zwei Transistoren auch fertig als Darlington-Transistor kaufen, von denen ebenfalls einige in der BD-Reihe zu finden sind (z.&amp;amp;nbsp;B. BD647). Ein Transistortyp, der sich sehr gut zum Schalten höherer Ströme eignet, ist der [[FET | MOSFET]].&lt;br /&gt;
&lt;br /&gt;
Wie schon oben angedeutet: Wenn die 30-80V die die meisten BC- und BD-Transistoren abkönnen, nicht ausreichen, suchst du weiter bei BU.&lt;br /&gt;
&lt;br /&gt;
Steigst du in die HF-Technik ein, sind BF-Transistoren eher das Richtige, wobei bei HF-Anwendungen die Auswahl der Transistoren nicht mehr das Schwierigste ist ;-)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Und wie geht&#039;s weiter?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Man könnte natürlich noch viel mehr zu diesem Thema schreiben. Ich hoffe aber, dass das Geschriebene dir wenigstens grob zeigt, wie man bei nicht allzu speziellem Anforderungen relativ schnell zu einem gewünschten Transistortyp kommt, der nicht nur die technischen Anforderungen erfüllt, sondern auch leicht beschaffbar ist.&lt;br /&gt;
&lt;br /&gt;
Werden die Anforderungen spezieller, helfen oft die Selektionstabellen auf den Webseiten der einschlägigen  Hersteller weiter. Auch Händler wie Farnell haben teilweise ganz gute Auswahlwerkzeuge. &lt;br /&gt;
&lt;br /&gt;
Wenn du dich intensiv mit Elektronik beschäftigst, wirst du wahrscheinlich noch viele  Schaltungen von Leuten zu Gesicht bekommen, die vielleicht schon etwas weiter fortgeschritten sind. Dabei wirst du immer wieder auf bestimmte Standardtypen von Transistoren (und auch anderen Bauteilen wie Operationsverstärker u.ä.) stoßen und sehen, welche [[Standardbauelemente]] &amp;quot;man&amp;quot; üblicherweise für bestimmte Anwendungen einsetzt. Mit der Zeit setzt sich dann eine Auswahl von bspw. 10 oder 20 verschiedenen Transistoren und 5 bis 10 verschiedenen OpAmps im Kopf fest, von denen man die wesentlichen Parameter auswendig kennt, so dass man ohne aufwendige Suche eine schnelle Auswahl treffen kann. Auch hier in der Artikelsammlung gibt es eine solche [[Transistor-Übersicht]].&lt;br /&gt;
&lt;br /&gt;
=== Wo ist die Antwort auf meine Frage? ===&lt;br /&gt;
&lt;br /&gt;
Vielleicht im Forum? Falls du sie da findest, dann pack das ganze doch hier rein.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/topic/165580 Gegen Vcc oder GND schalten]&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Basiswiderstand]]&lt;br /&gt;
* [[Transistor-Übersicht]]&lt;br /&gt;
* [[AVR_Transistortester]] - neu (Weiterentwickelt von Karl-Heinz Kübbeler)&lt;br /&gt;
* [[AVR-Transistortester]] - alte (von Markus Frejek)&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
== Gehäusebauformen von Transistoren ==&lt;br /&gt;
&lt;br /&gt;
=== TO-92 ===&lt;br /&gt;
Ein kleiner Aufsatz über TO-92 Transistorgehäuse und Footprints findet sich unter: [[Media:TO-92-Gehaeuse_RevB2.pdf]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://de.wikipedia.org/wiki/transistor &amp;quot;Transistor&amp;quot; bei Wikipedia]&lt;br /&gt;
* [http://www.elektronik-kompendium.de www.elektronik-kompendium.de]&lt;br /&gt;
** [http://www.elektronik-kompendium.de/sites/bau/0201291.htm Elko/Transistor]&lt;br /&gt;
* http://www.elektronikinfo.de/strom/bipolartransistoren.htm&lt;br /&gt;
* http://www.ferromel.de/tronic_1870.htm&lt;br /&gt;
* [http://www.DieElektronikerseite.de Die Elektronikerseite] Lehrgang: Der Transistor - Ein Tausendsassa&lt;br /&gt;
* [http://www.infoplease.com/encyclopedia/science/transistor-types-transistors.html Transistortypen]&lt;br /&gt;
* [http://electronics-electrical.exportersindia.com/electronic-components/transistors.htm Transistoren Industrieunternehmen Geschäftsauflistungen]&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Transistor&amp;diff=107006</id>
		<title>Transistor</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Transistor&amp;diff=107006"/>
		<updated>2024-07-10T17:08:27Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* PNP/NPN als Schalter, wohin mit der Last? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Kunstwort aus &amp;quot;transfer resistor&amp;quot;, was etwa so viel bedeutet wie &amp;quot;übertragener [[Widerstand]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In den 1950-ern als praktische Anwendung des [[Halbleiter]]-Effekts erfundenes &amp;quot;solid state&amp;quot; Schalt- und Verstärkerelement, welches sehr klein ist, ohne bewegte Teile auskommt (anders als ein klassisches Relais) und keine energiefressende Heizung benötigt (anders als eine Röhre).&lt;br /&gt;
&lt;br /&gt;
Vom &amp;quot;bipolaren Transistor&amp;quot; (PNP, NPN) weiterentwickelt zum &amp;quot;Feldeffekt-Transistor&amp;quot; ([[FET]]), der heute - gefertigt mit einem preiswerten Verfahren unter Verwendung von Metall-Oxid-Schichten (MOS) - ein wesentliches Element integrierter Schaltkreise (ICs, integrated circuits) darstellt, und damit natürlich auch von [[Mikrocontroller]]n, um die es in diesem Wiki hauptsächlich geht (bzw. gehen sollte).&lt;br /&gt;
&lt;br /&gt;
== Schaltzeichen ==&lt;br /&gt;
[https://electronicsclub.info/images/transbce.gif]&lt;br /&gt;
&lt;br /&gt;
* E: Emitter&lt;br /&gt;
* B: Basis&lt;br /&gt;
* C: Collector    &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In ASCII Schaltplänen sehen Transistoren so aus:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
                       |&amp;gt;                 |/&lt;br /&gt;
NPN      |&amp;gt;   oder    -|       oder      -|&lt;br /&gt;
                       |\                 |&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                       |&amp;lt;                 |/&lt;br /&gt;
PNP:     |&amp;lt;   oder    -|       oder      -|&lt;br /&gt;
                       |\                 |&amp;lt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um zu erkennen, ob ein NPN oder PNP Transistor im Schaltplan verwendet wird, gibt es Eselsbrücken:&lt;br /&gt;
*Für Dichter: &#039;&#039;&#039;Tut der Pfeil der Basis weh, handelt&#039;s sich um PNP.&#039;&#039;&#039;&lt;br /&gt;
*Für Praktiker: &#039;&#039;&#039;PNP heisst &amp;quot;Pfeil Nach Platte&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
*Mit Dialekt: &#039;&#039;&#039;NPN &amp;quot;&#039;naus, Pfeil, &#039;naus&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
*..und für Gleichberechtigungsverfechter: &#039;&#039;&#039;NPN means &amp;quot;Not Pointing iN&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
JFET: [[Bild:Transistor_JFET.png]]&lt;br /&gt;
&lt;br /&gt;
MOSFET: [[Bild:Transistor_MOSFET.png]]&lt;br /&gt;
&lt;br /&gt;
* S: Source&lt;br /&gt;
* G: Gate&lt;br /&gt;
* D: Drain&lt;br /&gt;
&lt;br /&gt;
Eigentlich haben MOSFETs noch einen vierten Anschluss namens Bulk. Der ist aber nur bei Spezialtypen als Pin herausgeführt. Im Normalfall kann man ihn vergessen, da er nicht gesondert beschaltet werden muss, er ist praktisch und auch im Symbol mit Source verbunden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Die NPN/PNP Eselsbrücken funktionieren bei FETs nicht, denn bei einem P-Kanal FET zeigt der Pfeil weg vom FET!&lt;br /&gt;
Ein Bipolartransistor, im Englischen als bipolar junction transistor (BJT) bezeichnet, ist ein Transistor, bei dem Ladungsträger – negativ geladene Elektronen und positiv geladene Defektelektronen – zum Stromtransport durch den Bipolartransistor beitragen. Der BJT wird mittels eines elektrischen Stroms gesteuert und wird zum Schalten und Verstärken von Signalen ohne mechanisch bewegte Teile eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Bipolare Leistungstransistoren sind für das Schalten und Verstärken von Signalen höherer Stromstärken und Spannungen ausgelegt.&lt;br /&gt;
&lt;br /&gt;
== Typbezeichnungen == &lt;br /&gt;
&lt;br /&gt;
Neben den Typbezeichnungen wie 2Nxxxx, TIPxxx, MJxxx, MJExx gibt es noch die in Europa geläufigere  Kennzeichnung bestehend aus zwei Buchstaben und drei Ziffern. Die diversen Kennzeichnungsmöglichkeiten sind in einem eigenen Artikel ([[Kennzeichnung von Halbleitern]]) zusammengefasst.&lt;br /&gt;
&lt;br /&gt;
== Kenndaten/Parameter ==&lt;br /&gt;
&lt;br /&gt;
Im [http://www.mikrocontroller.net/topic/197676#1938546 Beitrag: Transistorparameter Erklärung] sind Links zu Erläuterungen spezieller Kürzel.&lt;br /&gt;
&lt;br /&gt;
== Transistor Grundschaltungen ==&lt;br /&gt;
&lt;br /&gt;
Generell gilt, dass Strom vom Kollektor zum Emitter nur dann fließen kann, wenn die Basis positiver (NPN) bzw. negativer (PNP) wird als der Emitter. Dabei darf die Basis nicht direkt mit Vcc (NPN) oder GND (PNP) verbunden werden, da der Basisstrom sonst zu gross wird. Es muss jeweils ein geeigneter Basiswiderstand (R_Basis) gewählt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* Artikel [[Basiswiderstand]]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0203111.htm Transistor Grundschaltungen im ElKo]&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Transistor Transistor bei RoboterNetz.de]&lt;br /&gt;
&lt;br /&gt;
Es gibt drei Grundschaltungen. Der Name beschreibt den Anschluss, welcher sich auf einem festen Potential (Spannung) befindet. Die beiden anderen Anschlüsse haben bedingt durch die Schaltung ein veränderliches Potential.&lt;br /&gt;
&lt;br /&gt;
=== Kollektorschaltung (Emitterfolger)===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anwendung:&#039;&#039;&#039;&lt;br /&gt;
* Impedanzwandler&lt;br /&gt;
* Darlington-Schaltung&lt;br /&gt;
* Schalter&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Keine Phasendrehung&lt;br /&gt;
* Hohe Stromverstärkung&lt;br /&gt;
* Keine Spannungsverstärkung&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel: Transistor als Schalter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* NPN: Kollektor mit Vcc verbinden, Last an Emitter&lt;br /&gt;
* PNP: Kollektor mit GND verbinden, Last an Emitter&lt;br /&gt;
&lt;br /&gt;
In diesem Fall regelt der Transistor die Spannungen am Emitter, daher wird die Last am Emitter angeschlossen. Die Spannung am Emitter entspricht immer der an der Basis minus 0,6V, sie folgt der Basisspannung, deswegen auch der Name Emitterfolger. Daher ist diese Schaltung nicht geeignet, um 12V mit 5V zu schalten. &lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[Bild:NPN_Collector.gif | framed | center | NPN Transistor in Kollektorschaltung]] &lt;br /&gt;
|| [[Bild:PNP_Collector.gif | framed | center | PNP Transistor in Kollektorschaltung]]  &lt;br /&gt;
|}&lt;br /&gt;
* NPN: Wird &amp;lt;math&amp;gt;R_{Poti}&amp;lt;/math&amp;gt; (Spannungsteiler) erhöht, &#039;&#039;&#039;steigt&#039;&#039;&#039; die Spannung &amp;lt;math&amp;gt;U_{Last}&amp;lt;/math&amp;gt; letztlich bis auf VCC-0,6V (Basis-Emitter-Übergang).&lt;br /&gt;
* PNP: Wird &amp;lt;math&amp;gt;R_{Poti}&amp;lt;/math&amp;gt; (Spannungsteiler) erhöht, &#039;&#039;&#039;sinkt&#039;&#039;&#039; Spannung an &amp;lt;math&amp;gt;U_{Last}&amp;lt;/math&amp;gt; letztlich bis auf 0,6V (Basis-Emitter-Übergang).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0204133.htm Kollektorschaltung im ElKo]&lt;br /&gt;
&lt;br /&gt;
=== Emitterschaltung ===&lt;br /&gt;
&lt;br /&gt;
Die Emitterschaltung bietet hohe Spannungs- und Stromverstärkung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anwendung:&#039;&#039;&#039;&lt;br /&gt;
* NF- und HF-Verstärker&lt;br /&gt;
* Leistungsverstärker&lt;br /&gt;
* Transistor als Schalter&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Phasendrehung 180°&lt;br /&gt;
* Hohe Spannungsverstärkung&lt;br /&gt;
* Hohe Stromverstärkung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel: Transistor als Schalter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Last liegt am Kollektor. Der Strom durch den Schalter oder an U_Schalt steuert den Strom zwischen Kollektor und Emitter. Wird der Schalter geschlossen, fließt ein Strom.&lt;br /&gt;
&lt;br /&gt;
[[Bild:NPN_Schalter.gif | framed | center | NPN Transistor als Schalter]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                   ___                                          ___&lt;br /&gt;
Vcc/+ o-----------------------+          Vcc/+ o-----------------------+&lt;br /&gt;
                              |                                        |&lt;br /&gt;
                     ___    |&amp;lt;            U_schalt (-)       ___     |&amp;lt;  PNP             &lt;br /&gt;
             +------|___|---|  PNP             o------------|___|----|&lt;br /&gt;
             |     R_Basis  |\                             R_Basis   |\&lt;br /&gt;
    Schalter \                |                                        |&lt;br /&gt;
             |       ___      |                               ___      |&lt;br /&gt;
GND/- o------+------|___|-----+          GND/- o-------------|___|-----+&lt;br /&gt;
                   R_Last                                    R_Last     &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0204302.htm Emitterschaltung im ElKo]&lt;br /&gt;
&lt;br /&gt;
=== Basisschaltung ===&lt;br /&gt;
&lt;br /&gt;
Die Basisschaltung findet sich vor allem in Eingangsstufen in der HF-Technik. Im Schaltbetrieb wird sie praktisch nur zur [[Pegelwandler#STEP-UP: 5V -&amp;gt; 9..15V | Pegelwandlung]] für nachfolgende Stufen verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften:&#039;&#039;&#039;&lt;br /&gt;
* Geringe Eingangsimpedanz&lt;br /&gt;
* Keine Phasenverschiebung&lt;br /&gt;
* Hohe Bandbreite&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere Links:&#039;&#039;&#039;&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/slt/0205081.htm Basisschaltung im ElKo&lt;br /&gt;
&lt;br /&gt;
== FAQ aus dem Forum ==&lt;br /&gt;
&lt;br /&gt;
=== PNP/NPN als Schalter, wohin mit der Last? === &lt;br /&gt;
Für viele einfache Anwendungen kann man sich merken: &#039;&#039;&#039;Bei Schaltanwendungen darf der Basisstrom nicht durch die Last fließen&#039;&#039;&#039; (… wenn die zu schaltende Spannung höher ist als die Steuerspannung). Normalerweise kommt dabei die Emitterschaltung zum Einsatz, die Last kommt also an den Kollektor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 Vcc o-------------+                  Vcc o-----------+&lt;br /&gt;
                   |                                  |&lt;br /&gt;
                  .-.               An: GND   ___   |&amp;lt;&lt;br /&gt;
                  | | R_Last             o---|___|--|   PNP&lt;br /&gt;
                  &#039;-&#039;               Aus: Vcc        |\&lt;br /&gt;
                   |                                  |&lt;br /&gt;
An: Vcc  ___     |/                                  .-.&lt;br /&gt;
    o---|___|----|   NPN                             | | R_Last&lt;br /&gt;
Aus: GND         |&amp;gt;                                  &#039;-&#039;&lt;br /&gt;
                   |                                  |&lt;br /&gt;
 GND o─────────────┘                  GND o-----------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe [[Basiswiderstand]] zur Berechnung des notwendigen Basiswiderstandes bei gegebener Last R_Last für einen Transistor als Schalter.&lt;br /&gt;
&lt;br /&gt;
Siehe auch Threads im Forum: &lt;br /&gt;
* http://www.mikrocontroller.net/topic/58567 &lt;br /&gt;
* http://www.mikrocontroller.net/topic/119841&lt;br /&gt;
* oder im [http://www.elektronik-kompendium.de/sites/slt/0208031.htm Elektronik-Kompendium-Forum]&lt;br /&gt;
&lt;br /&gt;
Ist die zu schaltende Spannung &#039;&#039;&#039;genauso hoch&#039;&#039;&#039; wie die Steuerspannung darf die Last an den Emitter, und der Basiswiderstand entfällt. Der Transistor arbeitet als &#039;&#039;Impedanzwandler&#039;&#039; und steigert die Treiberfähigkeit des Mikrocontroller-Ausgangs in den Ampere-Bereich. Dieser Trick funktioniert &#039;&#039;&#039;nur&#039;&#039;&#039; mit Bipolartransistoren.&lt;br /&gt;
&lt;br /&gt;
Ist die effektive Invertierung des Schaltzustandes im oben angegebenen Bild lästig &#039;&#039;&#039;und&#039;&#039;&#039; der Strom geringer als der Ausgangsstrom, kann man den Transistor am Emitter steuern. Eine solche Schaltung funktioniert noch besser mit (Logik-Level-)MOSFETs und stellt eine einfache Kopplung zwischen Systemen mit unterschiedlicher Betriebsspannung (bspw. 1,8 V und 3,3 V) her. MOSFETs werden dazu am Source-Pin gesteuert, während das Gate an der „gegenüberliegenden“ Speisespannung „fixiert“ wird.&lt;br /&gt;
&lt;br /&gt;
=== Wie kann ich mit 5V vom Mikrocontroller 12V und mehr schalten? === &lt;br /&gt;
Schau mal hier:&lt;br /&gt;
* Wikiartikel [[Pegelwandler]]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/praxis/bausatz_pegelwandler-mit-transistoren.htm Pegelwandler im ElKo]&lt;br /&gt;
* [http://dl6gl.de/grundlagen/schalten-mit-transistoren schalten-mit-transistoren]&lt;br /&gt;
&lt;br /&gt;
oder in diesen Threads:&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/17899 Transistor als Schalter]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/14437 Vcc schalten mit MOSFET]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/29830 Schalten mit PNP-Transistor]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/104027 Transistorschalter für Versorgungsspannung]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/104951#921417 7-50V strombegrenzt schalten]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/103116#900247 P-Kanal MOSFET ansteuern]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/383039#4367479 5V/15mA mit 3,3V schalten]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/401317#4638354 High Side Treiber mit Ladungspumpe]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/347885?goto=3855574#3855574 minus -12V mit 3,3V schalten]&lt;br /&gt;
*[https://www.mikrocontroller.net/topic/516373?goto=6650986#6650986 Kurzschlußfester Schalter für VCC]&lt;br /&gt;
&lt;br /&gt;
Bei der Kollektor-Schaltung entspricht die Spannung am Emitter immer der an der Basis, daher ist sie nur bedingt geeignet. Zum Schalten können die folgenden Emitter-Schaltungen verwendet werden. Achtung: In der zweiten davon arbeitet T1 in Basisschaltung.&lt;br /&gt;
&lt;br /&gt;
Schalten gegen GND&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 +12V o------------------------+&lt;br /&gt;
                               |&lt;br /&gt;
                              .-. &lt;br /&gt;
                             ( X )  &lt;br /&gt;
                              &#039;-&#039;&lt;br /&gt;
                               |&lt;br /&gt;
                    ___      |/ T1,NPN   &lt;br /&gt;
        uC PIN o---|___|-----| BC547     &lt;br /&gt;
                   R2,4K7    |&amp;gt;&lt;br /&gt;
                               |&lt;br /&gt;
  GND o---------o--------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Schalten gegen +12V&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 +12V o--------------+----------------------+&lt;br /&gt;
                     |                      |&lt;br /&gt;
                     |   ____              |&amp;lt; T2, PNP&lt;br /&gt;
                     +--|____|----+--------|  BC557&lt;br /&gt;
                        R1,4K7    |        |\&lt;br /&gt;
                                |/T1,NPN    |&lt;br /&gt;
         Vcc/+5V o--------------| BC547     |&lt;br /&gt;
                                |&amp;gt;          |&lt;br /&gt;
                        ___       |        .-. &lt;br /&gt;
          uC PIN o-----|___|------+       ( X )  &lt;br /&gt;
                       R2,4K7              &#039;-&#039;&lt;br /&gt;
                                            |&lt;br /&gt;
  GND o----------o--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Transistor an µC ohne Vorwiderstand === &lt;br /&gt;
&lt;br /&gt;
Normalerweise sind IO Pins vom µC nicht in der Lage, große Ströme zu treiben, beim AVR maximal ~20mA. Für einen kleinen Transistor ist das immer noch zu viel und es wäre auch Stromverschwendung.&lt;br /&gt;
&lt;br /&gt;
Deshalb kann man den IO-Pin des AVRs einfach als Tristate Eingang einstellen (Portpin als Eingang und Pullup deaktivieren), damit kein Basisstrom fließt.&lt;br /&gt;
&lt;br /&gt;
Aktiviert man nun den internen Pullup-Widerstand des AVRs, agiert dieser als Basisvorwiderstand, und es fließt nur ein geringer Basisstrom (die Pullups eines AVRs liegen irgendwo bei 50k bis 100k Ohm - siehe Datenblatt).&lt;br /&gt;
&lt;br /&gt;
Nur sollte man bei kleinen Transistoren aufpassen, dass man den Portpin in der Software nie als aktiven Ausgang schaltet. &lt;br /&gt;
&lt;br /&gt;
Wenn der verwendete µC zuschaltbare Pulldown-Widerstände an seinen Pins besitzt, kann man das gleiche auch mit einem PNP-Transistor machen (natürlich nur den Pulldown aktivieren).&lt;br /&gt;
&lt;br /&gt;
Eine Anwendung wären z.&amp;amp;nbsp;B. Nixie-Röhren-Kathodentreiber (geringe Stromverstärkung nötig).&lt;br /&gt;
&lt;br /&gt;
=== Wann bipolare (NPN/PNP) und wann FETs (insbesonders, wenn LEDs im Spiel sind)?=== &lt;br /&gt;
Oft sind bipolare Transistoren (NPN/PNP) schon ausreichend, vor allem wenn &amp;quot;normale&amp;quot; LEDs (20mA) verwendet werden. FETs sind u.a. dann gut, wenn mit geringen Eingangsströmen hohe Ausgangsströme (über 300 mA) geschaltet werden sollen, also bei den Power-LEDs (Luxeon...).&lt;br /&gt;
&lt;br /&gt;
Ein Grenzfall: 500mA/5V schalten, siehe http://www.mikrocontroller.net/topic/62327.&lt;br /&gt;
&lt;br /&gt;
=== Wieso gehen bei einer Multiplex-Anzeige mit Schieberegister 74HC595 und (Darlington-)Transistor als Zeilentreiber die LED nicht ganz aus?  ===&lt;br /&gt;
Das liegt an der &#039;&#039;&#039;Miller-Kapazität&#039;&#039;&#039; des übersteuerten (Darlington-)Transistors. Der braucht erst mal einige 10µs, um zu sperren. Du mußt also erstmal beide Zeilen ausschalten, dann etwas warten und dann die nächste Zeile an. Oder Du ersetzt die Darlington durch P-FETs. [http://www.mikrocontroller.net/topic/132545]&lt;br /&gt;
&lt;br /&gt;
=== Wie steuert man ein Relais? ===&lt;br /&gt;
Normalerweise verwendet man zur Ansteuerung von Relais NPN-Transistoren in Emitterschaltung. Freilaufdiode nicht vergessen! &lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Relais mit Logik ansteuern]]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/22023/Relaisanteuerung.png Schaltbilder aus dem Forum]&lt;br /&gt;
&lt;br /&gt;
=== Was ist die Spannung &amp;lt;math&amp;gt;U_{BE\_sat}&amp;lt;/math&amp;gt; (lt. Datenblatt max. 1,2V)? ===&lt;br /&gt;
Bekanntlich verhält sich die Basis-Emitter Strecke eines Transistors wie eine Diode und &amp;lt;math&amp;gt;U_{BE\_sat}&amp;lt;/math&amp;gt; ist die bei maximal zulässigem Basisstrom anliegende Vorwärtsspannung.&lt;br /&gt;
&lt;br /&gt;
=== Was bewirkt ein Kondensator (100µF-1nF) parallel zur Basis-Emitter-Strecke nach Masse? === &lt;br /&gt;
Er wirkt mit dem Basisvorwiderstand als RC-Tiefpass. Damit wird der Transistor eigentlich nicht mehr als Schalter, sondern als Linearregler betrieben. Manche Verstärker-Schaltungen sind, gerade bei hohen Lasten, sehr schwingfreudig. Deswegen ist bei PWM so ein C nicht sinnvoll.&lt;br /&gt;
&lt;br /&gt;
=== Gibt es einen IC, der wie mehrere Transistoren funktioniert? ===&lt;br /&gt;
&lt;br /&gt;
Gibt es! Beispielsweise der &#039;&#039;&#039;ULN2803&#039;&#039;&#039; ist ein 8-fach Darlington Transistor Array mit [[Ausgangsstufen_Logik-ICs#Open_Collector|Open-Collector Ausgang]]. Damit lässt sich z.&amp;amp;nbsp;B. ein Leistungstreiber zur Ansteuerung von Schrittmotoren, Relais und anderen induktiven Lasten aufbauen.&lt;br /&gt;
&lt;br /&gt;
=== Gibt es ein Transistor-Array wie ULN28xx, das gegen Vcc schaltet? ===&lt;br /&gt;
&lt;br /&gt;
Such mal nach UDN29xx, z.&amp;amp;nbsp;B. UDN2981, UDN2987 ...&lt;br /&gt;
&lt;br /&gt;
Allegro hat zwischenzeitlich seinen Source-Treiber umbenannt. Die Bauteile heißen jetzt A2981, A2982 usw... Die Datenblätter sind identisch geblieben.&lt;br /&gt;
&lt;br /&gt;
Nachteil eines Darlington-Source-Treibers ist die hohe Sättigungsspannung (VceSat) von typisch 1,2 V. Die Eingangsspannung sollte dementsprechend höher ausgelegt werden oder gleich ein Source-Treiber mit MOSFET-Transistoren gewählt werden (z.B. Infineon ProFETs).&lt;br /&gt;
&lt;br /&gt;
=== Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein? ===&lt;br /&gt;
siehe [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
Die Summe allen Übels ist konstant. Man muss wissen, welches Bauteil sich wofür besonders eignet.&lt;br /&gt;
&lt;br /&gt;
====[[FET | MOSFET]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Bei niedrigen Spannungen &amp;lt;100V sehr gut geeignet, &amp;lt;200V gut geeignet, sehr geringe R_DS-ON Widerstände möglich (einstelliger mOhm-Bereich)&lt;br /&gt;
* Hohe Schaltgeschwindigkeiten möglich&lt;br /&gt;
* Geringe An- und Ausschaltverluste&lt;br /&gt;
* Statisch praktisch leistungslos steuerbar&lt;br /&gt;
* Bodydiode kann als Freilaufdiode in H-Brücken verwendet werden&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Drain-Source Sperrspannung zerstört das Bauteil nicht, wenn der Strom sowie die Energie begrenzt werden. (Verhalten wie [[Diode | Z-Diode]])&lt;br /&gt;
   &lt;br /&gt;
Nachteile&lt;br /&gt;
*Antiparallele Diode (Bodydiode) ist in nahezu allen MOSFETs unvermeidlich, daduch Sperren nur in einer Polarität möglich, Stromfluss über den MOSFET aber in beiden Richtungen möglich (Inversbetrieb, Synchrongleichrichter)&lt;br /&gt;
* Bei Sperrspannungen &amp;gt;100V deutlich steigende Einschaltwiderstände (R_DS-ON)&lt;br /&gt;
* Schnelles Umschalten erfordert hohe Lade-und Entladeströme ([[MOSFET-Übersicht#Mosfet-Treiber|MOSFET-Treiber]])&lt;br /&gt;
* Leitverluste quadratisch proportional zum Strom, Pv = I²*R_DS-ON&lt;br /&gt;
&lt;br /&gt;
====[[Transistor | Bipolartransistor]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Hohe Spannungsfestigkeiten möglich, bis zu 2000V&lt;br /&gt;
* Sehr hohe Schaltgeschwindigkeiten möglich&lt;br /&gt;
* Leitverluste etwa linear proportional zum Strom und Kollektor-Emitter-Sättigungsspannung, Pv = I * Uce-sat, U_CEC-sat typ 0,1...2.5 V&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Stromgesteuert, damit wird immer eine gewisse Ansteuerleistung benötigt&lt;br /&gt;
* Bei grossen Kollektorströmen nimmt die Stromverstärkung deutlich ab, dann wird ein großer Basisstrom benötigt&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Kollektor-Emitter Sperrspannung zerstört das Bauteil&lt;br /&gt;
&lt;br /&gt;
====[[IGBT]]====&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* Hohe Spannungsfestigkeiten möglich, bis zu 6600V, in üblichen Bauformen bis ca 1700V gut verfügbar&lt;br /&gt;
* Statisch praktisch leistungslos steuerbar&lt;br /&gt;
* Mit oder ohne antiparallele Diode zur Kollektor-Emitter-Strecke verfügbar&lt;br /&gt;
* Leitverluste linear proportional zum Strom und Kollektor-Emitter-Sättigungsspannung, Pv = I * Uce-sat, U_CE-sat typ. 2V&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Nur mäßige Schaltfrequenzen möglich (typ. &amp;lt;30..50kHz)&lt;br /&gt;
* Schnelles Umschalten erfordert hohe Lade-und Entladeströme ([[MOSFET-Übersicht#Mosfet-Treiber|MOSFET-Treiber]])&lt;br /&gt;
* 2. Durchbruch bei Überschreiten der max. Kollektor-Emitter Sperrspannung zerstört das Bauteil&lt;br /&gt;
&lt;br /&gt;
====[[TRIAC]]/Thyristor====&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Sehr hohe Spannungsfestigkeiten möglich (800V...mehrere kV, Thyristoren bis 12kV)&lt;br /&gt;
* Mit kurzen Pulsen einschaltbar, danach Selbsthaltung des Stromflusses&lt;br /&gt;
* Leitverluste linear proportional zum Strom, Pv = I * Uak, Uak typ. 0,8...1,5 V &lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Auf niedrige Schaltfrequenzen beschränkt (kHz-Bereich, Schaltzeit im Bereich von µs)&lt;br /&gt;
* Anstiegsgeschwindigkeit des Stromes muss begrenzt werden, sonst kommt es zu Bauteilschäden&lt;br /&gt;
* Anstiegsgeschwindigkeit der Spannung muss begrenzt werden, sonst kommt es zum ungewollten Zünden&lt;br /&gt;
* Stromfluss kann nicht ausgeschaltet werden, damit meist nur Einsatz an Wechselspannung&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50em&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Bauteil               &lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | optimales&amp;lt;br&amp;gt;Einsatzgebiet&lt;br /&gt;
! Kommentar&lt;br /&gt;
|-&lt;br /&gt;
| MOSFET                || 0..200V, 0..500A || im Kleinspannungsbereich meist die beste Wahl als Schalter&lt;br /&gt;
|-&lt;br /&gt;
| Bipolartransistor     || 0..1000V, 0..10A || wird mehr und mehr von MOSFETs verdrängt&lt;br /&gt;
|-&lt;br /&gt;
| IGBT                  || 200..1700V, 0..500A || optimal für hohe Spannungen und hohe Ströme&lt;br /&gt;
|-&lt;br /&gt;
| Triac/Thyristor       || 230V, 400V, 680V,&amp;lt;br&amp;gt;bis mehrere kV, 0..100A || meist für Wechselspannung, &amp;lt;br&amp;gt;Thyristoren bis 1000A im Dauerbetrieb, &amp;lt;br&amp;gt;im Pulsbetrieb einige kA. (Scheibenzelle)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Wie finde ich den richtigen Transistor für eine LED-Ansteuerung? ===&lt;br /&gt;
&lt;br /&gt;
Quelle: Beiträge [http://www.mikrocontroller.net/topic/157763#1493623] und [http://www.mikrocontroller.net/topic/157763#1494972] von yalu&lt;br /&gt;
&lt;br /&gt;
Um am Anfang wenigstens ein bisschen den Durchblick im Transistordschungel zu behalten, kannst du folgendermaßen vorgehen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nomenklatur&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nach der amerikanischen Nomenklatur beginnen die Transistornamen meist mit 2N (z.&amp;amp;nbsp;B. 2N2222 oder 2N3055) und nach der japanischen mit 2S (z.&amp;amp;nbsp;B. 2SC1815). Für den Anfang kann man sich auf europäische Transistoren beschränken, da es diese in ausreichender Auswahl gibt und die Bezeichnungen relativ gut den Transistortyp wiedergeben:&lt;br /&gt;
&lt;br /&gt;
Der erste Buchstabe bezeichnet das Halbleitermaterial (A=Germanium, B=Silizium). Germaniumtransistoren werden heute nur noch selten verwendet.&lt;br /&gt;
&lt;br /&gt;
Der zweite Buchstabe steht für den Einsatzzweck (C=Universal, D=hohe Leistung, F=Hochfrequenz, U=hohe Spannung).&lt;br /&gt;
&lt;br /&gt;
So ist also ein ACxxx ein Germaniumuniversaltransistor und ein BDxxx ein Siliziumleistungstransistor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auswahl&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn du dich für einen Grundtyp entschieden hast (für die LED ist ein BC-Typ das Richtige), gehst du auf die Webseite eines Elektronikhändlers (Reichelt, Kessler usw.), schlägst die Seite mit den BC-Transistoren auf. Da gibt es natürlich sehr viele  davon, und du brauchst jetzt eine&lt;br /&gt;
Suchreihenfolge. Als erstes Auswahlkriterium nimmst du den Preis, denn:&lt;br /&gt;
&lt;br /&gt;
* Zuviel Geld hast wahrscheinlich nicht einmal du.&lt;br /&gt;
* Billig ist meist das, was in großen Stückzahlen hergestellt wird. Was für die Masse gut ist, ist (zumindest in diesem Fall) meist auch für dich gut.&lt;br /&gt;
* Was billig und damit in Massen verkauft wird, bekommst du auch bei anderen Händlern und auch noch in 10 Jahren. Das ist wichtig, wenn deine Schaltung irgendwann einmal in Serie gefertigt werden soll.&lt;br /&gt;
&lt;br /&gt;
Gleich als nächstes überlegst du, ob du einen NPN- oder einen PNP-Typ brauchst. Das ergibt sich aus der Anordnung der Bauteile in deiner Schaltung. Hast du die Möglichkeit, die Schaltung wahlweise für einen NPN- oder einen PNP-Typ auszulegen, wählst du die Variante mit dem NPN-Typ. Um einfach eine LED über einen Mikrocontroller einzuschalten, ist i.Allg. ein NPN-Typ in Emitterschaltung richtig.&lt;br /&gt;
&lt;br /&gt;
Ein weiteres wichtiges Kriterium ist die Befestigungstechnik: Wenn dir die SMD-Löterei etwas suspekt ist, lässt du die entsprechenden Modelle erst einmal alle außen vor. Ein typisches Nicht-SMD-Gehäuse für Universaltransistoren ist TO-92. Es gibt im Internet bebilderte Listen mit den einzelnen Gehäuseformen ([[IC-Gehäuseformen#Weblinks]])&lt;br /&gt;
&lt;br /&gt;
Wenn du jetzt also bei Reichelt  die BC-Transistoren nach Preis aufsteigend sortiert hast, siehst du erst einen Schwung SMD-Tranistoren. Dann kommen ein paar Transistoren im TO-92-Gehäuse, die sind aber PNP. Etwas weiter unten kommt der erste NPN-Transistor in TO-92, nämlich der BC547C. Netterweise stehen gleich ein paar Eckdaten dabei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
BC547C  45V  0,1A  0,5W&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die 45V sind die maximale Kollektor-Emitter-Spannung, in deinem Fall also die Spannung, die du maximal schalten kannst. Da die LED bei Weitem keine 45V braucht und deine Versorgungsspannung eher in der Gegend von 5V liegt, bist du auf jeden Fall auf der sicheren Seite.&lt;br /&gt;
&lt;br /&gt;
Deine LED wird typisch mit 20mA (max. 30mA) betrieben. Der BC547C kann 100mA, also ist auch hier noch Luft.&lt;br /&gt;
&lt;br /&gt;
Zur maximalen Verlustleistung (0,5W): Wenn deine LED eingeschaltet ist, fließen bspw. 20mA. Ist der Transistor voll durchgesteuert (in Sättigung) beträgt die Kollektor-Emitter-Spannung bei diesem geringen Strom typischerweise zwischen 0,1V und 0,2V (Genaueres steht im Datenblatt). Am Transistor wird also maximal die Leistung 20mA·0,2V=4mW in Wärme umgesetzt. Bis zu 500mW dürfen es sein, also ebenfalls ok.&lt;br /&gt;
&lt;br /&gt;
Nachdem du den Transistor in engere Auswahl gezogen hast, lohnt sich auf jeden Fall ein Blick ins Datenblatt. Aus den Tabellen und Diagrammen erfährst du bspw., wie hoch der Basisstrom sein muss, um den Kollektorstrom von mindestens 20mA bei ausreichend geringer CE-Spannung bereitzustellen. Dort ist auch erklärt warum es einen BC547A, BC547B und BC547C gibt. Der letzte Buchstabe gibt nämlich die Stromverstärkungsklasse an. Da eine hohe Stromverstärkung meist wünschenswert ist und in diesem Fall keinen Aufpreis kostet, ziehst du den BC547C den anderen beiden vor.&lt;br /&gt;
&lt;br /&gt;
Da in deiner Anwendung HF- und Rauschverhalten keine Rolle spielen, bist du schon am Ziel angelangt.&lt;br /&gt;
&lt;br /&gt;
Würde deine LED 100mA statt 20mA benötigen, wären die max. 100mA des BC547 etwas knapp bemessen. Du blätterst also in der Reichelt-Liste weiter und stößt auf den BC337-40 mit 45V, 0,5A und 0,525W. Das ist genau das, wonach du suchst. Bei diesem Transistor sind die Stromverstärkungsklassen durch die Endungen -16, -25 und -40 gekennzeichnet. Es wäre ja auch&lt;br /&gt;
zu einfach, wenn immer nur A, B und C verwendet würde ;-)&lt;br /&gt;
&lt;br /&gt;
Bei Strömen ab etwa 500mA kommt man an die Grenze der Leistungsfähigkeit der BC-Typen. Dann geht es weiter mit BD. Der BD135 geht bspw. schon bis 1,5A. Das Problem bei solchen größeren Transistoren: Die Stromverstärkung ist nicht besonders hoch, so dass irgendwann der Mikrocontroller nicht mehr den benötigten Basisstrom liefern kann. Dann muss dem großen Transistor ein kleiner vorangeschaltet werden, um den erhöhten Basisstrom bereitszustellen. Man kann diese Kombination von zwei Transistoren auch fertig als Darlington-Transistor kaufen, von denen ebenfalls einige in der BD-Reihe zu finden sind (z.&amp;amp;nbsp;B. BD647). Ein Transistortyp, der sich sehr gut zum Schalten höherer Ströme eignet, ist der [[FET | MOSFET]].&lt;br /&gt;
&lt;br /&gt;
Wie schon oben angedeutet: Wenn die 30-80V die die meisten BC- und BD-Transistoren abkönnen, nicht ausreichen, suchst du weiter bei BU.&lt;br /&gt;
&lt;br /&gt;
Steigst du in die HF-Technik ein, sind BF-Transistoren eher das Richtige, wobei bei HF-Anwendungen die Auswahl der Transistoren nicht mehr das Schwierigste ist ;-)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Und wie geht&#039;s weiter?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Man könnte natürlich noch viel mehr zu diesem Thema schreiben. Ich hoffe aber, dass das Geschriebene dir wenigstens grob zeigt, wie man bei nicht allzu speziellem Anforderungen relativ schnell zu einem gewünschten Transistortyp kommt, der nicht nur die technischen Anforderungen erfüllt, sondern auch leicht beschaffbar ist.&lt;br /&gt;
&lt;br /&gt;
Werden die Anforderungen spezieller, helfen oft die Selektionstabellen auf den Webseiten der einschlägigen  Hersteller weiter. Auch Händler wie Farnell haben teilweise ganz gute Auswahlwerkzeuge. &lt;br /&gt;
&lt;br /&gt;
Wenn du dich intensiv mit Elektronik beschäftigst, wirst du wahrscheinlich noch viele  Schaltungen von Leuten zu Gesicht bekommen, die vielleicht schon etwas weiter fortgeschritten sind. Dabei wirst du immer wieder auf bestimmte Standardtypen von Transistoren (und auch anderen Bauteilen wie Operationsverstärker u.ä.) stoßen und sehen, welche [[Standardbauelemente]] &amp;quot;man&amp;quot; üblicherweise für bestimmte Anwendungen einsetzt. Mit der Zeit setzt sich dann eine Auswahl von bspw. 10 oder 20 verschiedenen Transistoren und 5 bis 10 verschiedenen OpAmps im Kopf fest, von denen man die wesentlichen Parameter auswendig kennt, so dass man ohne aufwendige Suche eine schnelle Auswahl treffen kann. Auch hier in der Artikelsammlung gibt es eine solche [[Transistor-Übersicht]].&lt;br /&gt;
&lt;br /&gt;
=== Wo ist die Antwort auf meine Frage? ===&lt;br /&gt;
&lt;br /&gt;
Vielleicht im Forum? Falls du sie da findest, dann pack das ganze doch hier rein.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/topic/165580 Gegen Vcc oder GND schalten]&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Basiswiderstand]]&lt;br /&gt;
* [[Transistor-Übersicht]]&lt;br /&gt;
* [[AVR_Transistortester]] - neu (Weiterentwickelt von Karl-Heinz Kübbeler)&lt;br /&gt;
* [[AVR-Transistortester]] - alte (von Markus Frejek)&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
== Gehäusebauformen von Transistoren ==&lt;br /&gt;
&lt;br /&gt;
=== TO-92 ===&lt;br /&gt;
Ein kleiner Aufsatz über TO-92 Transistorgehäuse und Footprints findet sich unter: [[Media:TO-92-Gehaeuse_RevB2.pdf]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://de.wikipedia.org/wiki/transistor &amp;quot;Transistor&amp;quot; bei Wikipedia]&lt;br /&gt;
* [http://www.elektronik-kompendium.de www.elektronik-kompendium.de]&lt;br /&gt;
** [http://www.elektronik-kompendium.de/sites/bau/0201291.htm Elko/Transistor]&lt;br /&gt;
* http://www.elektronikinfo.de/strom/bipolartransistoren.htm&lt;br /&gt;
* http://www.ferromel.de/tronic_1870.htm&lt;br /&gt;
* [http://www.DieElektronikerseite.de Die Elektronikerseite] Lehrgang: Der Transistor - Ein Tausendsassa&lt;br /&gt;
* [http://www.infoplease.com/encyclopedia/science/transistor-types-transistors.html Transistortypen]&lt;br /&gt;
* [http://electronics-electrical.exportersindia.com/electronic-components/transistors.htm Transistoren Industrieunternehmen Geschäftsauflistungen]&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107005</id>
		<title>FET</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107005"/>
		<updated>2024-07-10T16:51:40Z</updated>

		<summary type="html">&lt;p&gt;Heha: /* Erklärung der wichtigsten Datenblattwerte */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel versteht sich als Unterpunkt zum Artikel [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein FET (engl. &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor) ist ein  Feldeffekttransistor. Der FET ist ein Bauelement, das im Gegensatz zum Bipolartransistor (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT) mit Spannung und nicht mit Strom gesteuert wird. Unterschieden werden&lt;br /&gt;
* MOSFET = engl. &#039;&#039;&#039;M&#039;&#039;&#039;etall &#039;&#039;&#039;O&#039;&#039;&#039;xide &#039;&#039;&#039;S&#039;&#039;&#039;emiconductor &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor; Metalloxidschicht-FET, größte Teilgruppe der FETs mit isoliertem Gate &lt;br /&gt;
* JFET = engl. &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, Übergangszonen FET, der steuerbare Kanal wird durch einen PN-Übergang wie in einer Diode gebildet&lt;br /&gt;
&lt;br /&gt;
Die drei Anschlüsse eines FETs werden &#039;&#039;Gate&#039;&#039;, &#039;&#039;Drain&#039;&#039; und &#039;&#039;Source&#039;&#039; genannt. Unter Umständen ist ein vierter Anschluß vorhanden, der &#039;&#039;Bulk&#039;&#039; genannt wird. Normalerweise ist Bulk intern mit Source verbunden. Wenn dies nicht der Fall ist, muss diese Verbindung durch den Designer in der Schaltung hergestellt werden.&lt;br /&gt;
&lt;br /&gt;
== FET-Typen ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
FETs werden hauptsächlich unterschieden in N-Kanal und P-Kanal, sowie &amp;quot;selbst sperrend = Anreicherungstyp&amp;quot; (engl. enhancement type) und &amp;quot;selbst leitend = Verarmungstyp&amp;quot; (engl. depletion type). Beim selbstleitenden FET ist der Transistor bei 0V Gate-Source Spannung maximal leitend (durchgesteuert) und wird durch Anlegen einer Spannung ans Gate gesperrt. Beim selbstsperrenden FET (größte Gruppe) ist der Transistor bei 0V Gate-Source Spannung gesperrt und wird durch Anlegen einer Spannung ans Gate leitend. Ist die Linie zwischen Drain und Source durchgezogen handelt es sich um einen selbstleitenden, bei einer gestrichelten Linie um einen selbstsperrenden FET. JFETs gibt es nur als Verarmungstyp. Im weiteren Artikel wird nur mehr der &amp;quot;selbstsperrende&amp;quot; MOSFET betrachtet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Typen von Feldeffekttransistoren&amp;lt;br/&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Typ &lt;br /&gt;
! N-Kanal &lt;br /&gt;
! P-Kanal&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| JFET  || [[bild:JFET-N.png|center]]&lt;br /&gt;
* drittgrößte Gruppe&lt;br /&gt;
* bislang nur für kleine Leistungen verfügbar&lt;br /&gt;
* JFETs mit hoher Leistung sind im Kommen&lt;br /&gt;
* Eingangsstufen von OPVs&lt;br /&gt;
* Eingangsstufen von HF-Verstärkern bis in den GHz-Bereich&lt;br /&gt;
* als einfache [[Konstantstromquelle]] geeignet&lt;br /&gt;
| [[bild:JFET-P.png|center]]&lt;br /&gt;
* selten &lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Anreicherungstyp&amp;lt;br/&amp;gt;(selbst sperrend) || [[bild:MOS-EN.png|center]]&lt;br /&gt;
* größte Gruppe&lt;br /&gt;
* sehr viele Typen erhältlich&lt;br /&gt;
| [[bild:MOS-EP.png|center]]&lt;br /&gt;
* zweitgrößte Gruppe&lt;br /&gt;
* bei gleicher Geometrie etwas schlechter als ein N-Kanal Typ&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Verarmungstyp&amp;lt;br/&amp;gt;(selbst leitend) || [[bild:MOS-DN.png|center]]&lt;br /&gt;
* selten&lt;br /&gt;
| [[bild:MOS-DP.png|center]]&lt;br /&gt;
* sehr selten&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Vorteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Niedrigere Verluste als bei Bipolartransistoren.&lt;br /&gt;
* Sehr schnelles Schalten möglich, daher für sehr hohe Frequenzen geeignet (keine Speicherzeit wie beim BJT).&lt;br /&gt;
* Einfaches Parallelschalten im Schaltbetrieb, da Unterschiede im &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; durch den positiven Temperaturkoeffizienten ausgeglichen werden.&lt;br /&gt;
* Leistungslose Ansteuerung im statischen Fall, jedoch hohe Umladeverluste am Gate!&lt;br /&gt;
* oft preiswerter als vergleichbare Bipolartransistoren (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT)&lt;br /&gt;
* Relativ unempfindlich gegen Überspannung zwischen Drain und Source. Bei Überschreitung der Maximalspannung zwischen Drain und Source findet ein sogenannter &amp;quot;Durchbruch&amp;quot; statt. Dies ist vergleichbar mit dem Zener-Effekt. Ist die Energiemenge begrenzt, ist dieser Durchbruch reversibel und der FET wird NICHT zerstört. Beim Bipolartransistor ist die dafür zulässige Energiemenge viel kleiner, weil die den Strom durchquerende Fläche zu Hotspots neigt.&lt;br /&gt;
&lt;br /&gt;
=== Nachteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Nur bedingt für hohe Spannungen [[Transistor#Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein ? |geeignet]], die ON-Verluste sind ab ca. 250V höher als bei einem [[IGBT]]. &lt;br /&gt;
* Parasitäre Diode parallel zur Drain-Source Strecke ist immer enthalten, das (Ab-)Schaltverhalten dieser Dioden ist meist schlechter als separate Dioden, was häufig zu unerwünschten Schwingungen führt.&lt;br /&gt;
* Empfindlicher gegen ESD am Gate als BJT&lt;br /&gt;
* Positiver Temperaturkoeffizient (TK), der &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; ist stark temperaturabhängig und steigt von 25°C (Datenblattangabe) auf 150°C ungefähr um den Faktor 2. Dadurch steigen auch die Verluste und damit die Erwärmung des Bauteiles.&lt;br /&gt;
&lt;br /&gt;
=== Erklärung der wichtigsten Datenblattwerte ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:7em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | Beispiel&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Drain Source Voltage &amp;lt;br&amp;gt;(Breakdown) || V(BR)_DSS &amp;lt;br&amp;gt; V_DS || 75 V || Maximale Spannungsfestigkeit des Bauteiles zwischen Drain und Source&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Drain Current  || I_D(on)   || 55 A @ 125 °C  || Maximaler Dauerstrom bei 125°C Gehäusetemperatur &lt;br /&gt;
|-&lt;br /&gt;
| Pulsed Drain Current || ID_pulse &amp;lt;br&amp;gt; I_CD(on) || 240 A || Maximaler Pulsstrom (Achtung die zulässige Zeitdauer des Pulses kann nur über die maximale Junctiontemperatur ermittelt werden)&lt;br /&gt;
|-&lt;br /&gt;
| Gate Charge || Q&amp;lt;sub&amp;gt;GS&amp;lt;/sub&amp;gt; ||  320 nC || Gate-Ladung in Coloumb oder Amperesekunden, die zum Ein- oder Ausschalten des MOSFET erforderlich ist. Dies berücksichtigt die Nichtlinearität der Kapazität und (richtig?) den Miller-Effekt. Mit dieser Angabe wird der für das Schalten von Leistungs-MOSFET benötigte Gatestrom für eine bestimmte Schaltzeit ermittelt. Dieser liegt üblicherweise im einstelligen Ampere-Bereich. Moderne Typen mit extra geringer Gate-Ladung können so schneller schalten.&lt;br /&gt;
|-&lt;br /&gt;
| Repetetive Avalanche Energy || t_sc ||  280 mJ || Maximale Energie, welche beim Avalanche Durchbruch bei Überschreiten der maximalen Drain-Source Spannung im MOSFET bei z.&amp;amp;nbsp;B. 1% Puls/Pausen Verhältnis regelmäßig auftreten darf, ohne den FET zu schädigen&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_ON&amp;lt;/sub&amp;gt; ||  0,01 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;25°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R&amp;lt;sub&amp;gt;DS_on&amp;lt;/sub&amp;gt; ||  0,021 Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;175°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Thermal Resistance&amp;lt;br&amp;gt;(junction-case) ||  R&amp;lt;sub&amp;gt;th_JC&amp;lt;/sub&amp;gt; ||  0,8 K/W || Thermischer Widerstand im Transistor vom der „Sperrschicht“ (junction eines Bipolartransistors, ja altmodischer Index, hier besser „Kanal“) bis zur Rückseite oder bestmöglichen Kühlmöglichkeit des Transistorgehäuses (case)&lt;br /&gt;
|-&lt;br /&gt;
| Gate-Source&amp;lt;br&amp;gt;Threshold Voltage ||  V_GS(th) || 2,0-4,5 V || Gatespannung, ab welcher der Transistor minimal leitend wird (I_D typisch 100-200µA), große Toleranz, typisch 1:2 zwischen Minimum und Maximum&lt;br /&gt;
|-&lt;br /&gt;
| Turn-on Delay ||  t_d(on) || 40 ns ||  Verzögerung zwischen dem Einschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Rise Time ||  t_r || 200 ns ||  Anstiegszeit des Transistorstromes am Drain&lt;br /&gt;
|-&lt;br /&gt;
| Turn-off Delay || t_d(off) || 120 ns ||  Verzögerung zwischen Abschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Fall Time ||  t_f  || 60 ns || Abfallzeit des Transistorstromes am Drain  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die oben genannten Zeiten gelten ausschließlich unter den angegebenen Messbedingungen (Gatewiderstand, Treiberspannung, sowie einer &#039;&#039;&#039;FET-Teperatur von 25°C!&#039;&#039;&#039;) und müssen für die eigene Anwendung ggf. neu berechnet werden. Meist wird man sie eher messen, weil die Rechung zu aufwändig und bisweilen unmöglich ist. &lt;br /&gt;
&lt;br /&gt;
==== Gate-Source Threshold Voltage ====&lt;br /&gt;
Gerade bei der &#039;&#039;&#039;Gate-Source Threshold Voltage &amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt;&#039;&#039;&#039; gibt es hier immer wieder Verwirrung. Sie gibt an, ab welcher Spannung der MOSFET anfängt, minimal leitfähig zu werden. Diese Spannung ist technologisch bedingt auch heute noch einer starken Toleranz unterworfen, typischerweise hat der Bereich  bei dem der FET zu leiten beginnt eine Spreizung von etwa 1:2 zwischen Minimum und Maximum. Welche Spannung man nun wirklich anlegen muss, um den gewünschten &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt; zu erreichen kann der Tabelle im jeweiligen Datenblatt entnommen werden. Dabei unbedingt die angegebene Gatespannung beachten, nur dieser Wert ist garantiert!.&lt;br /&gt;
&lt;br /&gt;
[[bild: IRLZ34N_R_DS_ON.png | thumb | 800px | R_DS_ON im Datenblatt des IRLZ34N]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Die Kurvenschar von &amp;lt;math&amp;gt;I_D&amp;lt;/math&amp;gt; über &amp;lt;math&amp;gt;U_{DS}&amp;lt;/math&amp;gt; in Abhängigkeit von &amp;lt;math&amp;gt;U_{GS}&amp;lt;/math&amp;gt; stellt immer nur typische Werte dar, keine garantierten Extremwerte (engl. worst case). Als Standardwerte kann man 10-15V für einen Standardtypen und ca. 3-5V für einen Logic Level MOSFET (LL-FET) ansetzen. Kleinsignal-FETs leiten schon ab ca 1V. Bei Ansteuerung mit 5V benötigt man also einen Typen, der &#039;&#039;&#039;sicher&#039;&#039;&#039; bei 5V voll durchgesteuert ist, z.B. IRLZ34N. Bei 3,3V ist er bereits nicht mehr zuverlässig nutzbar. Es gibt auch Typen mit noch geringerer Spannung für Vollaussteuerung. Wer einen BUZ11 (&amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt; 4V max.) mit 5V ansteuert riskiert ein Abfackeln des MOSFETs, denn je nach Toleranz kann er bereit ganz gut aufgesteuert sein oder auch nicht. Das soll in den nachfolgenden Diagrammen dargestellt werden. Zunächst die Transferkennlinie. Sie gibt an, wieviel Drainstrom in Abhängigkeit der Gatespannung fließen kann, wobei die Drainspannung konstant ist, hier im Beispiel 25V. Ein typischer BUZ11 (mittlere, schwarze Kurve) fängt bei 3V zu leiten an und erreicht bei 6V am Gate ca. 17A. Erwischt man nun ein kritisches Exemplar, das erst bei 4V zu leiten anfängt (rechte, rote Kurve um 1V parallel verschoben), so kann dieser bei 6V nur 8A leiten, für 17A braucht er 7V. Der günstige Fall, daß der BUZ11 schon bei 2V anfängt zu leiten sieht man in der linken, roten Kurve.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UGS_ID.png | thumb | 800px | Transferkennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Wenn man die drei Fälle (min, typ, max) mit 5V ansteuert, erhält man die Arbeitspunkte 1-3 mit einem maximal schaltbaren Drainstrom von 2,5, 8 und 17A. Diese kann man in das Ausgangskennlinienfeld übertragen. Wir nehmen max. 1V U_DS an. Es ergeben sie die gleichen Ströme. Der maximale Drainstrom weicht um -5,5/+9A vom typischen Fall ab. Erhöht man nun die Gate-Source Spannung auf 10V (Arbeitspunkt 4), schwankt der maximal schaltbare Drainstrom nur noch um -2/+2A, außerdem liegt er mit 30A deutlich höher. Der BUZ11 ist somit sicher durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UDS_ID_UGS.png | thumb | 800px | Ausgangskennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
==== Parasitäre Diode ====&lt;br /&gt;
&lt;br /&gt;
Der Schwerpunkt in der FET-Entwicklung liegt auf den geringst-möglichen &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt;,  die Diode entsteht auf Grund des Herstellungsprozesses, und wird nur nachrangig verbessert, da viele Optimierungsversuche auch einen Einfluss auf wichtige Kennwerte des FETs hatten und haben.&lt;br /&gt;
Daher muss sorgfältig geprüft werden, ob die Schaltgeschwindigkeit, die Recovery-Time und die damit verbundenen Verluste sowie die dabei erzeugte unerwünschte EMV-Abstahlung tolerierbar ist, oder nicht.&lt;br /&gt;
Hier hilft es oft eine optimierte Diode / Schottky-Diode zum FET parallel zu schalten. Ganz ausblenden läßt sich die parasitäre Diode jedoch nicht, jedoch kann man den Anteil des Stromes beeinflussen, der über die intere Diode fliest.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039; Parasitäre Diode des FETs  &#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Current  ||  I_S || 75A || Maximaler Dauerstrom der parasitären Diode, meist identisch zum maximalen Dauerstrom des MOSFETs&lt;br /&gt;
|-&lt;br /&gt;
| Forward Voltage ||  V_SD || 1,3V || Spannungsfall an der parasitären Diode &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Time ||  t_rr || 120ns || Zeit, welche die Elektronen brauchen um aus der leitenden Diode vollständig abzufließen. Während dieser Zeit fließt der Strom in &#039;&#039;&#039;Rückwärtsrichtung&#039;&#039;&#039; durch die Diode und erzeugt relativ viel Verlustleistung. &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Charge ||  Q_rr || 60nC || Ladungsmenge, die während t_rr rückwärts durch die Diode fließt.  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Haupttypen und Gatespannungslevel ==&lt;br /&gt;
&lt;br /&gt;
===Unterschied N-Kanal / P-Kanal FET===&lt;br /&gt;
&lt;br /&gt;
Im Schaltsymbol werden die MOSFET-Typen meist durch die Pfeilrichtung in der Mitte des Symbols (eigentlich &amp;quot;Bulk&amp;quot;) vom oder zum Gate unterschieden.  Zeigt der Pfeil zum Gate hin, handelt es sich um einen N-Kanal-FET, zeigt der Pfeil vom Gate weg um einen P-Kanal FET.&lt;br /&gt;
&lt;br /&gt;
Der große Vorteil des N-Kanal FETs (Elektronenleitung) ist, daß er immer niederohmiger ist, als ein gleich großer P-Kanal FET (Löcherleitung). Daher sind P-Kanal Typen bei vergleichbaren Werten auch immer größer = teuerer da weniger Chips auf einem Wafer Platz haben.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET muss die Gatespannung positiv gegenüber Source sein. Dabei wird der FET dann leitend, wenn die sogenannte &amp;quot;threshold voltage&amp;quot; (Schwellenspannung) erreicht wird. Eine typische Anwendung ist z.&amp;amp;nbsp;B. ein &#039;&#039;&#039;Low-Side Schalter&#039;&#039;&#039;: Source an GND, Drain an die Last, Ansteuerung des N-Kanal FETs mit 12V gleichbedeutend mit 12V ÜBER den Source = GND Potential.&lt;br /&gt;
 &lt;br /&gt;
Beim P-Kanal FET als HS-Schalter muss die Gatespannung negativer=niedriger als das Sourcepotential sein.Beispiel.&lt;br /&gt;
Beispiel:  &lt;br /&gt;
Lastspannung = 400V d.h. Source an 400V, Last zwischen Drain und GND, Ansteuerung des P-Kanal FETs mit 388V, also 12V UNTER dem Sourcepotential.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET als HS-Schalter muss die Gatespannung positver=höher als das Sourcepotential sein.&lt;br /&gt;
Beispiel:&lt;br /&gt;
Lastspannung = 400V d.h. Drain an 400V, die Last zwischen Source und GND, Ansteuerung des N-Kanal FETs mit 412V, also 12V ÜBER dem Sourcepotential.&lt;br /&gt;
In diesem Fall ist aber eine zusätzliche Spannungsquelle erforderlich, denn der FET wird mit einer Spannung über der Lastspannung eingeschaltet. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weblinks&#039;&#039;&#039;&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-1-of-2/?utm_source=eetimes&amp;amp;utm_medium=networksearch A primer on high-side FET load switches (Part 1 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products, Micrel, Inc., 5/3/2007 4:14 PM EDT, www.eetimes.com&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-2-of-2/ A primer on high-side FET load switches (Part 2 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products. Micrel, Inc., 5/7/2007 1:36 PM EDT, www.eetimes.com&lt;br /&gt;
* [http://www.vishay.com/docs/70611/70611.pdf AN804 P-Channel MOSFETs, the Best Choice for High-Side Switching (PDF)] von Vishay Siliconix&lt;br /&gt;
&lt;br /&gt;
===Unterschied Logic-Level / &amp;quot;Normal&amp;quot;-Level===&lt;br /&gt;
&lt;br /&gt;
Den meisten FETs ist gemein, daß sie mit einer Spannung von 10..15V angesteuert werden müssen, um den minimalen Einschaltwiderstand zu erreichen. Diese FETs lassen sich nicht ohne weiteres mit einem CMOS-Pegel von 5V ansteuern. Es gibt jedoch für diesen Anwendungsfall sogenannte &amp;quot;Logic Level&amp;quot; (LL) FETs, die schon bei einer Gatespannung von etwa 4,5V voll durchgesteuert sind. Einige Kleinsignal-FETs sind schon ab ca. 1,2V voll durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
== Beispiel zur Bauteiledimensionierung ==&lt;br /&gt;
&lt;br /&gt;
=== Spannungsfestigkeit ===&lt;br /&gt;
&lt;br /&gt;
Die höchste vorkommende Betriebsspannung + Abschaltüberspannung soll kleiner als ca. 80% der Spannungsfestigkeit des Bauteiles sein. &lt;br /&gt;
&lt;br /&gt;
Achtung: Zwischen dem je nach Anwendungsfall erforderlichen Pufferkondensator und dem FET wird es immer eine parasitäre Induktivität geben.&lt;br /&gt;
Abhängig von Schaltgeschwindigkeit und Induktivität wird im Schaltmoment eine mehr oder weniger große Übrspannungsspitze produziert. Dieser Peak&lt;br /&gt;
addiert sich auf die aktuelle Versorgungsspannung.&lt;br /&gt;
&lt;br /&gt;
Überschlagsrechnung als Beispiel:&lt;br /&gt;
* Schaltgeschwindigkeit:  dI/dt = -100A/µs (= Abschalten von 5A innerhalb 50ns),&lt;br /&gt;
* Induktivität:   L = 1µH (~ 1 m loses, ungebündeltes Kabel)&lt;br /&gt;
* dU=-L*dI/dt = -1µH * (-100A / 1µs) = 100V&lt;br /&gt;
&lt;br /&gt;
Dies bedeutet, daß an der &amp;quot;Induktivität&amp;quot; zwischen Transistor und Kondensator - Aufgrund von Selbstinduktion im Schaltmoment - ein Überspannungspuls von ca. 100V entsteht, der auf die Betriebsspannung aufzuschlagen ist, also lieber die Leitung kürzer machen, und - sofern möglich - nicht ganz so schnell schalten.&lt;br /&gt;
&lt;br /&gt;
=== Stromtragfähigkeit ===&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt ist eine Stromtragfähigkeit bei 25°C, und meist noch bei einer höheren Temperatur z.B. 125°C, 150°C oder 175°C Kühlfahnentemperatur angegeben. Dieser Wert ist als ERSTE Entscheidungsgrundlage ausreichend, aber aus der theoretisch abführbaren Verlustleistung errechnet, und&lt;br /&gt;
* dient zum qualitativen Vergleich von Transistoren bezüglich ihres R_ds(on) und ihres Wärmewiderstands.&lt;br /&gt;
* ist für die Dimensionierung einer Schaltung nur als Richtwert zu interpretieren. &lt;br /&gt;
* ist ohne Schaltverluste genannt, und daher nur für einen Schaltbetrieb von wenigen Hz gültig. Außerdem wird ein annähernd idealer Kühlkörper unterstellt, der trotz der Verlustleistung das Gehäuse des Transistors auf der angegebenen Temperatur halten kann.&lt;br /&gt;
* entbindet einen nicht davon den Kopf einzuschalten... siehe die nachfolgenden Zeilen.&lt;br /&gt;
* Liegt der Strom für den die Schaltung entwickelt wird mit ca. 10..20% Abstand unter dem Datenblattwert von 125°C ist dieses Bauteil vermutlich verwendbar (siehe Detailberechnungen unten !).   &lt;br /&gt;
* Ist der benötigte Strom im Bereich oder größer als der zulässige bei 125°C sollte entweder ein anderer Typ eingesetzt oder mehrere FETs &#039;&#039;des gleichen Typs&#039;&#039; parallelgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
== Verlustleistung ==&lt;br /&gt;
&lt;br /&gt;
Hier wird eine Näherung für eine getaktete Anwendung betrachtet. In einem Transistor treten sowohl beim Ein- und Ausschalten, als auch während der Einschaltphase Verluste im Bauteil auf. Diese Verluste führen zu einer Bauteilerwärmung. Die dabei entstehende Temperatur darf die maximal zulässige Bauteiletemperatur nie überschreiten. Bei den ersten Projekten ist zu empfehlen eine berechnete Chiptemperatur von ca. 125°C nicht zu überschreiten. Fast alle aktuell verfügbaren FETs nennen im Datenblatt eine Temperatur von 175°C als ihre maximale Chiptemperatur.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:32em&amp;quot; &lt;br /&gt;
|+ &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Symbol&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Wert&lt;br /&gt;
|-&lt;br /&gt;
| Betriebsspannung || U&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 70 V&lt;br /&gt;
|-&lt;br /&gt;
| Nennstrom || I&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 30 A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source Widerstand bei&amp;lt;br&amp;gt;Chiptemperatur: 125°C&amp;lt;br&amp;gt; Gatespannung: 10V || R&amp;lt;sub&amp;gt;DS&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt;&amp;lt;/sub&amp;gt; || 17 mΩ&lt;br /&gt;
|-&lt;br /&gt;
| Pulsbreite || t&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt; || 150 µs&lt;br /&gt;
|-&lt;br /&gt;
| Schaltfrequenz || ƒ&amp;lt;sub&amp;gt;schalt&amp;lt;/sub&amp;gt; || 5 kHz&amp;lt;br&amp;gt;T = 200µs&lt;br /&gt;
|-&lt;br /&gt;
| Einschaltzeit (risetime) || t&amp;lt;sub&amp;gt;r&amp;lt;/sub&amp;gt; || 500 ns&lt;br /&gt;
|-&lt;br /&gt;
| Ausschaltzeit (falltime) || t&amp;lt;sub&amp;gt;ƒ&amp;lt;/sub&amp;gt; || 800 ns &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Leitend-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Während der FET bei [[PWM]]-Ansteuerung eingeschaltet ist, erzeugt er Verlustleistung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
P_\text{ON}&lt;br /&gt;
 = {I_\mathrm{N}}^2 \cdot R_\mathrm{DS_\mathrm{ON}} \cdot \frac{t_\mathrm{ON}}{T}&lt;br /&gt;
 = 30^2A^2 \cdot 17m\Omega  \cdot \frac{150\mu s}{200\mu s} = 11{,}5W&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schalt-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Vereinfachter Ansatz.&lt;br /&gt;
&lt;br /&gt;
Einschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_r}&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_r}{T} \\&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot 70V \cdot 30A \cdot \frac{500ns}{200\mu s}=1{,}3W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_f} &lt;br /&gt;
&amp;amp;=\tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_f}{T}\\&lt;br /&gt;
&amp;amp;=\tfrac14 \cdot 70V \cdot 30A \cdot \frac{800ns}{200\mu s}=2{,}1W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ und genauer kann man rechnen, wenn die Ein- Ausschaltenergie im Datenblatt angegeben ist. Aber Achtung! Die  Randbedingungen unter denen die genannte Energie ermittelt wurde, müssen genau so zutreffen.&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_f} = f_{schalt} \cdot E_{ON}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_r} = f_{schalt} \cdot E_{OFF}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Gesamtverlustleistung beträgt also in etwa 15W.&lt;br /&gt;
&lt;br /&gt;
Damit muß ein entsprechender [[Kühlkörper]] ausgelegt und die Chiptemperatur berechnet werden. z.&amp;amp;nbsp;B.:&lt;br /&gt;
* Kühlkörper mit einem R_th von 0,2K/W&lt;br /&gt;
* max. Umgebungstemperatur +60°C&lt;br /&gt;
* R_th &amp;quot;junction-case&amp;quot; des FETs 0,8K/W&lt;br /&gt;
* R_th der Wärmeleitfolie zwischen FET und Kühlkörper ca. 2,0K/W&lt;br /&gt;
* R_th gesamt: 3,0K/W &amp;lt;br&amp;gt;&lt;br /&gt;
* Bei einer Verlustleistung von 18W und einer Umgebungstemperatur von 60°C hat der Chip eine Temperatur von ca. 18W * 3,0K/W +60°C = 114°C. ==&amp;gt; o.k.!&lt;br /&gt;
&lt;br /&gt;
Unter Berücksichtigung der Tatsache, daß hier viele Vereinfachungen vorgenommen, und die Art der Last nicht beachtet wurde ist es sinnvoll, einen gewissen Sicherheitsabstand zu den zulässigen Maximalwerten einzuhalten. Daher ist es empfehlenswert, die Chiptemperatur auf ca. 125°C zu beschränken. &lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist hier die parasitäre Diode im FET nicht berücksichtigt.&lt;br /&gt;
Wenn während der &amp;quot;off&amp;quot; Zeit ein Strom über die Diode fließt (Reverse recovery current oder Freilaufstrom), muß die dadurch &#039;&#039;&#039;zusätzlich&#039;&#039;&#039; entstehende Verlustleistung in die obige Berechnung der maximalen Chiptemperatur mit einfließen.&lt;br /&gt;
&lt;br /&gt;
==Treiberleistung==&lt;br /&gt;
&lt;br /&gt;
Auch wenn der MOSFET ein spannungsgesteuertes Bauelement ist, muss trotzdem bei jedem Einschalten und bei jedem Ausschalten die Gatekapazität umgeladen werden. Bei älteren Leistungs-FET - oder bei einem schlechten Design (!) - muss sogar teilweise mit negativer Spannung am Gate gearbeitet werden, um eine vollständige Sperrung zu erreichen.&lt;br /&gt;
Diese Umladung muss möglichst schnell erfolgen, um die Verluste im FET während der Umschaltphase zu minimieren. Dazu findet ein [[Mosfet-Übersicht#MOSFET-Treiber|MOSFET-Treiber]] Verwendung. Hier eine detaillierte Beschreibung zum [[Treiber]].&lt;br /&gt;
&lt;br /&gt;
Da die Gatekapazität nicht direkt im Datenblatt enthalten ist kann man sich mit der Eingangskapazität Ciss behelfen. Im Arbeitspunkt ist die Gatekapazität ungefähr 5x größer als der im Datenblatt für Ciss angegebene Wert. &lt;br /&gt;
Daher berechnet sich die Treiberleistung wie folgt: &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{Treiber} = C \cdot U^2 \cdot f = 5 \cdot C_\text{íss} \cdot U_\text{Gate}^2 \cdot f_\text{schalt}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1.Beispiel, kleine MOSFET-Steuerung mit niedriger Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 4{,}8\,\text{nF} \cdot 15\,\text{V}^2 \cdot 10\,\text{kHz} = 54\,\text{mW}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2.Beispiel, sehr große MOSFET-Steuerung für Induktionsheizung mit sehr hoher Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 24\,\text{nF} \cdot 15\,\text{V}^2 \cdot 250\,\text{kHz} = 6{,}75\,\text{W}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung, so ein MOSFET-Treiber hat auch einen Eigenverbrauch, der leicht zwischen 0,5 und 1 W liegen kann.&lt;br /&gt;
&lt;br /&gt;
Bei niedrigen PWM-Frequenzen kann man Logic Level MOSFETs auch direkt per CMOS-Ausgang ansteuern, z.B. mit einem [[AVR]], wie in diesem [http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag] zu sehen ist.&lt;br /&gt;
&lt;br /&gt;
== Low- und High-Side ==&lt;br /&gt;
&lt;br /&gt;
Definition LS- und HS:&lt;br /&gt;
 &lt;br /&gt;
;Low-Side Schalter: Der FET schaltet eine Last gegen GND - auch als LS-Schalter bezeichnet.&lt;br /&gt;
;High-Side Schalter: Der FET schaltet eine Last an die Versorgungsspannung – auch als HS-Schalter bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Anregungen oder Fragen auch gerne per Email an [http://www.mikrocontroller.net/user/show/powerfreak Powerfreak]. Dieser Artikel kann dadurch regelmäßig erweitert und ggf. durch ein FAQ ergänzt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== SOA Diagramm ==&lt;br /&gt;
&lt;br /&gt;
SOA-Diagramm (engl. &#039;&#039;&#039;S&#039;&#039;&#039;afe &#039;&#039;&#039;O&#039;&#039;&#039;perating &#039;&#039;&#039;A&#039;&#039;&#039;rea, sicherer Arbeitsbereich) beschreibt die zulässige Verlustleistung eines Transistors in Anhängigkeit des Drainstroms (I_D), der Drain-Source Spannung (U_DS) und der Pulsbreite. Als Beispiel sei hier der BUZ 11 genannt. Im nachfolgenden Diagramm ist das SOA-Diagramm dargestellt. Wie ist es zu verstehen? Zunächst gibt es eine Grenze auf der linken Seite, die schräge, dunkelblaue Line. Diese wird durch den minimalen R_DS_ON festgelegt, hier wirkt der MOSFET wie ein ohmscher Widerstand. Mehr Strom kann bei einer bestimmten Spannung nicht fließen. Die zweite Grenzlinie ist ganz rechts die pinkfarbene Linie, sie stellt die maximale Sperrspannung des MOSFET dar. Die dritte Grenze ist der maximal zulässige Drainstrom, hier im Beispiel 120A, dargestellt durch die gelbe Linie. Die maximale Spannung zwischen Drain und Source sowie der Drainstrom sind abhängig von der Pulsbreite, mit welcher der MOSFET betrieben wird. Bei nur 2,5µs Pulsbreite (Rechteckimpuls) müssen die beiden Parameter sich innerhalb der Fläche bewegen, welche durch die dunkelblaue, gelbe und die pinkfarbene Line begrenzt wird. Im Extremfall dürfen 50V anliegen und 120A fließen, das sind satte 6kW Pulsleistung! Werden die Pulse breiter, so sinken die zulässigen Ströme und Spannungen, bei 1ms (dunkelblaue Linie bis zur braunen Linie, dann zur pinkfarbenen Linie) sind maximal noch 50V und 7A zulässig, also nur noch 350W. Die letzte Linie stellt den Fall für Gleichstrom (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent), also Dauerbelastung dar, hier sind bei 50V maximal 1,5A zulässig, was einer Dauerverlustleistung von 75W entspricht. MOSFETs, welche nur für Schaltbetrieb und nicht für [[#Linearbetrieb von MOSFETs | Linearbetrieb]] geeignet sind, haben keine Kennlinie für DC. Im normalen Schaltbetrieb liegt der Arbeitspunkt auf der linken Grenzlinie R_DS_ON_MIN. Nur im Linearbetrieb liegt der Arbeitspunkt innerhalb der Fläche, welche durch die Außenlinien begrenzt wird.&lt;br /&gt;
&lt;br /&gt;
[[bild: SOA-BUZ11.png | thumb | 300px| SOA-Diagramm]]&lt;br /&gt;
&lt;br /&gt;
Bei der Anwendung des Diagramms gilt es einiges zu beachten. Die Pulsleistungen sind nur zulässig, wenn der MOSFET vorher kalt ist, sprich ca. 25°C Sperrschichttemperatur hat. War er vorher schon heiß, reduziert sich die zulässige Belastung deutlich. Ebenso dürfen die Pulse nicht zu schnell wiederholt werden, denn dann ist der MOSFET noch vom vorherigen Puls aufgeheizt. Im Fall von DC sind 75W Verlustleistung auch eher ein theoretischer Wert, welcher real nur schwer erreicht werden kann, wenn der MOSFET auf einem sehr großen [[Kühlkörper]] optimal montiert ist. Praktisch liegen die erreichbaren Werte eher bei der Hälfte.&lt;br /&gt;
&lt;br /&gt;
(Anm. Eigentlich müsste für die R_DS_ON Grenzlinie R = U / I der minimale R_DS_ON rauskommen, hier ~40mOhm, es kommen aber ~80mOhm raus. Die Ursache dafür ist unklar, möglicherweise liegt hier ein Sicherheitsfaktor zu grunde).&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Linearbetrieb von MOSFETs ==&lt;br /&gt;
&lt;br /&gt;
Der Großteil der Anwendungen nutzt MOSFETs als Schalter, d.h. der MOSFET ist entweder voll gesperrt oder voll durchgesteuert. Dafür gelten auch all die Hinweise in diesem Artikel. In bestimmten Anwendungen werden MOSFETs aber auch im Linearbetrieb eingesetzt, z.B in linearen Endstufen für Audio, Video, elektronischen Lasten und Stromquellen. Hier muss man einiges beachten. Ein verbreiteter Irrtum besteht darin zu glauben, MOSFETs könne man im Linearbetrieb einfach parallel schalten, weil der positive Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; eine Symmetrierung bewirkt, ähnlich den Emitterwiderständen bei parallelgeschalteten Bipolartransistoren. Das ist &#039;&#039;ausschließlich&#039;&#039; im Schaltbetrieb möglich, und daher falsch! Im Linearbetrieb spielt der Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; keine Rolle, weil der MOSFET selten bis nie komplett durchgesteuert ist. Eben darum ist beim Linearbetrieb der minimale &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; in den meisten Fällen unwichtig und man kann auch eher hochohmige, ältere MOSFETs verwenden, wie z.B. den BUZ11.&lt;br /&gt;
&lt;br /&gt;
Hier wirkt vielmehr der negative Temperaturkoeffizient (TK) der Thresholdspannung &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, vergleichbar dem negativen TK der Basis-Emitter-Spannung von Bipolartransistoren. D.h. mit steigender Temperatur und konstanter Gate-Source-Spannung steigt der Stromfluss der Drain-Source Strecke. In einer Parallelschaltung von MOSFETs würde dies bedeuten, dass der MOSFET mit dem geringfügig größeren Drainstrom (Fertigungstoleranzen) wärmer wird, was zu einem weiter steigenden Drainstrom und damit noch mehr Wärme führt. Damit ist die Schaltung thermisch instabil und würde zum Durchbrennen der MOSFETs führen, einer nach dem Anderen. &lt;br /&gt;
&lt;br /&gt;
Um das zu verhindern muss man relativ große Ausgleichswiderstände in die Source-Leitung der einzelnen MOSFETs schalten, um diese Drift zu kompensieren. Dadurch verschlechtert sich natürlich der Wirkungsgrad des Verstärkers. MOSFETs haben einen TK von typisch -5mV/K für &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, das ist mehr als das doppelte von Bipolartransistoren mit typisch -2mV/K, weshalb die Symmetrierungswiderstände mehr als doppelt so groß sein müssen. Weiterhin muss man beachten, dass die Toleranzen von &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt; sehr groß sind, im Bereich von Volt! Das kann man sinnvoll nicht mehr mit Gatewiderständen symmetrieren, hier muss man die MOSFETs ausmessen und Gruppen mit geringen Toleranzen in einer Schaltung verwenden (engl. matching). &lt;br /&gt;
&lt;br /&gt;
Eine andere Möglichkeit ist die getrennte Ansteuerung der einzelnen MOSFETs, das wird oft in elektronischen Lasten bzw. [[Konstantstromquelle#Konstantstromquelle mit Operationsverstärker und Transistor | Konstantstromquellen]] gemacht. Hier treten keine zusätzlichen Verluste auf und der Mehraufwand in der Ansteuerung ist meist unkritisch.&lt;br /&gt;
&lt;br /&gt;
Weiterhin muss man beachten, dass viele der heutigen HochleistungsMOSFETs intern eine Parallelschaltung vieler kleiner MOSFET-Zellen (z.B. sogenannte Trench-FET) sind, und somit oft für den Linearbetrieb ungeeignet sind. Denn auch dort können einzelne Zellen überhitzen und durchbrennen (Hot Spot). Ob ein MOSFET für den Linearbetrieb tauglich ist steht manchmal im Datenblatt, oft aber eher nicht, eben weil die meisten MOSFETs als Schalter entwickelt und gebaut sind. Typische Vertreter für Linearbetrieb findet man in der [[MOSFET-Übersicht]]. Ein wichtiges Indiz für Linearbetrieb ist eine Kurve für DC im [[#SOA_Diagramm | SOA-Diagramm]]. Meist geht es dort nur bis 10ms, DC fehlt, eben weil DC (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent = Gleichstrom = Linearbetrieb) nicht zulässig ist. Manchmal hat der Hersteller auch &amp;quot;vergessen&amp;quot;, die Kennlinie für DC mit reinzuschreiben, wie z.B. bei [http://www.irf.com/product-info/hi-rel/alerts/fv5-p-09-01-A.pdf IRF], wie in diesem [http://www.mikrocontroller.net/topic/291760#3106758 Beitrag] zu erfahren ist.&lt;br /&gt;
Ein recht gutes Indiz dafür, ob ein FET für den Linearbetrieb taugt, ist die Vorwärtssteilheit. Diese kennzeichnet die Abhängigkeit des Drainstromes von der Ansteuerung am Gate als &amp;lt;math&amp;gt;S = \Delta i_d/\Delta u_{gs}&amp;lt;/math&amp;gt;. Moderne Trench-FET erreichen heute Steilheiten im dreistelligen Bereich und sind für Linearanwendungen völlig unbrauchbar. Zum Vergleich: Der BUZ11 kommt mit gerade einmal 4 bis 5 Siemens daher.&lt;br /&gt;
&lt;br /&gt;
In diesem Beitrag wird die DC-Linie im SOA-Diagramm noch genauer erklärt: [http://www.mikrocontroller.net/topic/319961#3473567 Re: MOSFET Linearbetrieb möglich?]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
* [[Mosfet-Übersicht]]&lt;br /&gt;
* [[IGBT]]&lt;br /&gt;
* [[TRIAC]]&lt;br /&gt;
* [[Kühlkörper]] &lt;br /&gt;
* [[Zwischenkreiskapazität]]&lt;br /&gt;
* [[Treiber]]&lt;br /&gt;
* [[Snippets#Wie_schlie.C3.9Fe_ich_einen_MOSFET_an_einen_Mikrocontroller_an.3F|Wie schließe ich einen Mosfet an einen Mikrocontroller an?]]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/H-Br%C3%BCcken_%C3%9Cbersicht Übersicht H-Brücken]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/168218#1609684 Forumsbeitrag]: MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/186785#new Forumsbeitrag]: nochmal MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/319961#3473567 Forumsbeitrag]: sehr ausführlicher Forumsbeitrag über MOSFETs im Linearbetrieb. Berücksichtigt auch den Spirito-Effekt.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/143324#new Forumsbeitrag]: Über eine elektronische Last, sehr lang&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag]: Logic Level MOSFETs direkt mit einem [[AVR]] treiben.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787855 Forumsbeitrag]: MOSFETs im Linearbetrieb, Laborerfahrungen&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787945 Forumsbeitrag]: MOSFETs für Linearbetrieb&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/269642?goto=2820617#2820617 Forumsbeitrag]: Verpol- und Überspannungsschutz mit MOSFETs&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/425414#4981611 Forumsbeitrag]: Linearbetrieb von MOSFETs&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.elektronikinfo.de/strom/feldeffekttransistoren.htm Feldeffekttransistoren bei elektronikinfo.de]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0207011.htm FET im ELKO]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0510161.htm MOSFET im ELKO]&lt;br /&gt;
* [http://www.sprut.de/electronic/switch/nkanal/nkanal.html MOSFET bei sprut.de]&lt;br /&gt;
* [http://sound.westhost.com/articles/hexfet.htm#51 MOSFETs in Audioendstufen, engl.]&lt;br /&gt;
* [http://irf.custhelp.com/cgi-bin/irf.cfg/php/enduser/std_adp.php?p_faqid=214&amp;amp;p_created=1019728945&amp;amp;p_sid=pt9ITiCj&amp;amp;p_accessibility=0&amp;amp;p_redirect=&amp;amp;p_lva=&amp;amp;p_sp=cF9zcmNoPTEmcF9zb3J0X2J5PSZwX2dyaWRzb3J0PSZwX3Jvd19jbnQ9MTQsMTQmcF9wcm9kcz0mcF9jYXRzPSZwX3B2PSZwX2N2PSZwX3BhZ2U9MSZwX3NlYXJjaF90ZXh0PWxpbmVhcg**&amp;amp;p_li=&amp;amp;p_topview=1 FAQ Answer ID 214 bei IRF zum Linearbetrieb]&lt;br /&gt;
* [http://www.nxp.com/documents/application_note/AN11158.pdf AN11158 - Understanding power MOSFET data sheet parameters] von NXP (PDF)&lt;br /&gt;
* [https://assets.nexperia.com/documents/technical-note/TN00008.pdf TN00008 - Power MOSFET frequently asked questions and answers] von nexperia (PDF)&lt;br /&gt;
* [http://www.infineon.com/dgdl/Infineon+-+Application+Note+-+PowerMOSFETs+-+OptiMOS%E2%84%A2+-+Linear+Mode+Operation+and+SOA+Power+MOSFETs.pdf?fileId=db3a30433e30e4bf013e3646e9381200 AN: Linear Mode Operation andSafe Operating Diagram of Power-MOSFETs] von Infineon (PDF)&lt;br /&gt;
* [http://www.ixys.com/Documents/Articles/Article_Linear_Power_MOSFETs.pdf MOSFETs Withstand Stress of Linear-Mode Operation] Neuentwickelte MOSFETs für Linearbetrieb (PDF)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]] [[Kategorie:Leistungselektronik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107004</id>
		<title>FET</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FET&amp;diff=107004"/>
		<updated>2024-07-10T16:36:24Z</updated>

		<summary type="html">&lt;p&gt;Heha: Auch beim Bipolartransistor führt der &amp;quot;Second Breakdown&amp;quot; nicht zwangsläufig zu seinem Tod. Meistens schon.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel versteht sich als Unterpunkt zum Artikel [[Leistungselektronik]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein FET (engl. &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor) ist ein  Feldeffekttransistor. Der FET ist ein Bauelement, das im Gegensatz zum Bipolartransistor (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT) mit Spannung und nicht mit Strom gesteuert wird. Unterschieden werden&lt;br /&gt;
* MOSFET = engl. &#039;&#039;&#039;M&#039;&#039;&#039;etall &#039;&#039;&#039;O&#039;&#039;&#039;xide &#039;&#039;&#039;S&#039;&#039;&#039;emiconductor &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor; Metalloxidschicht-FET, größte Teilgruppe der FETs mit isoliertem Gate &lt;br /&gt;
* JFET = engl. &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;F&#039;&#039;&#039;ield &#039;&#039;&#039;E&#039;&#039;&#039;ffect &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, Übergangszonen FET, der steuerbare Kanal wird durch einen PN-Übergang wie in einer Diode gebildet&lt;br /&gt;
&lt;br /&gt;
Die drei Anschlüsse eines FETs werden &#039;&#039;Gate&#039;&#039;, &#039;&#039;Drain&#039;&#039; und &#039;&#039;Source&#039;&#039; genannt. Unter Umständen ist ein vierter Anschluß vorhanden, der &#039;&#039;Bulk&#039;&#039; genannt wird. Normalerweise ist Bulk intern mit Source verbunden. Wenn dies nicht der Fall ist, muss diese Verbindung durch den Designer in der Schaltung hergestellt werden.&lt;br /&gt;
&lt;br /&gt;
== FET-Typen ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
FETs werden hauptsächlich unterschieden in N-Kanal und P-Kanal, sowie &amp;quot;selbst sperrend = Anreicherungstyp&amp;quot; (engl. enhancement type) und &amp;quot;selbst leitend = Verarmungstyp&amp;quot; (engl. depletion type). Beim selbstleitenden FET ist der Transistor bei 0V Gate-Source Spannung maximal leitend (durchgesteuert) und wird durch Anlegen einer Spannung ans Gate gesperrt. Beim selbstsperrenden FET (größte Gruppe) ist der Transistor bei 0V Gate-Source Spannung gesperrt und wird durch Anlegen einer Spannung ans Gate leitend. Ist die Linie zwischen Drain und Source durchgezogen handelt es sich um einen selbstleitenden, bei einer gestrichelten Linie um einen selbstsperrenden FET. JFETs gibt es nur als Verarmungstyp. Im weiteren Artikel wird nur mehr der &amp;quot;selbstsperrende&amp;quot; MOSFET betrachtet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Typen von Feldeffekttransistoren&amp;lt;br/&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | Typ &lt;br /&gt;
! N-Kanal &lt;br /&gt;
! P-Kanal&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| JFET  || [[bild:JFET-N.png|center]]&lt;br /&gt;
* drittgrößte Gruppe&lt;br /&gt;
* bislang nur für kleine Leistungen verfügbar&lt;br /&gt;
* JFETs mit hoher Leistung sind im Kommen&lt;br /&gt;
* Eingangsstufen von OPVs&lt;br /&gt;
* Eingangsstufen von HF-Verstärkern bis in den GHz-Bereich&lt;br /&gt;
* als einfache [[Konstantstromquelle]] geeignet&lt;br /&gt;
| [[bild:JFET-P.png|center]]&lt;br /&gt;
* selten &lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Anreicherungstyp&amp;lt;br/&amp;gt;(selbst sperrend) || [[bild:MOS-EN.png|center]]&lt;br /&gt;
* größte Gruppe&lt;br /&gt;
* sehr viele Typen erhältlich&lt;br /&gt;
| [[bild:MOS-EP.png|center]]&lt;br /&gt;
* zweitgrößte Gruppe&lt;br /&gt;
* bei gleicher Geometrie etwas schlechter als ein N-Kanal Typ&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| MOSFET&amp;lt;br/&amp;gt;Verarmungstyp&amp;lt;br/&amp;gt;(selbst leitend) || [[bild:MOS-DN.png|center]]&lt;br /&gt;
* selten&lt;br /&gt;
| [[bild:MOS-DP.png|center]]&lt;br /&gt;
* sehr selten&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Vorteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Niedrigere Verluste als bei Bipolartransistoren.&lt;br /&gt;
* Sehr schnelles Schalten möglich, daher für sehr hohe Frequenzen geeignet (keine Speicherzeit wie beim BJT).&lt;br /&gt;
* Einfaches Parallelschalten im Schaltbetrieb, da Unterschiede im &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; durch den positiven Temperaturkoeffizienten ausgeglichen werden.&lt;br /&gt;
* Leistungslose Ansteuerung im statischen Fall, jedoch hohe Umladeverluste am Gate!&lt;br /&gt;
* oft preiswerter als vergleichbare Bipolartransistoren (engl. &#039;&#039;&#039;B&#039;&#039;&#039;ipolar &#039;&#039;&#039;J&#039;&#039;&#039;unction &#039;&#039;&#039;T&#039;&#039;&#039;ransistor, BJT)&lt;br /&gt;
* Relativ unempfindlich gegen Überspannung zwischen Drain und Source. Bei Überschreitung der Maximalspannung zwischen Drain und Source findet ein sogenannter &amp;quot;Durchbruch&amp;quot; statt. Dies ist vergleichbar mit dem Zener-Effekt. Ist die Energiemenge begrenzt, ist dieser Durchbruch reversibel und der FET wird NICHT zerstört. Beim Bipolartransistor ist die dafür zulässige Energiemenge viel kleiner, weil die den Strom durchquerende Fläche zu Hotspots neigt.&lt;br /&gt;
&lt;br /&gt;
=== Nachteile des FET ===&lt;br /&gt;
&lt;br /&gt;
* Nur bedingt für hohe Spannungen [[Transistor#Wann setzt man einen MOSFET, Bipolartransistor, IGBT oder Thyristor ein ? |geeignet]], die ON-Verluste sind ab ca. 250V höher als bei einem [[IGBT]]. &lt;br /&gt;
* Parasitäre Diode parallel zur Drain-Source Strecke ist immer enthalten, das (Ab-)Schaltverhalten dieser Dioden ist meist schlechter als separate Dioden, was häufig zu unerwünschten Schwingungen führt.&lt;br /&gt;
* Empfindlicher gegen ESD am Gate als BJT&lt;br /&gt;
* Positiver Temperaturkoeffizient (TK), der &amp;lt;math&amp;gt;R_{DS,on}&amp;lt;/math&amp;gt; ist stark temperaturabhängig und steigt von 25°C (Datenblattangabe) auf 150°C ungefähr um den Faktor 2. Dadurch steigen auch die Verluste und damit die Erwärmung des Bauteiles.&lt;br /&gt;
&lt;br /&gt;
=== Erklärung der wichtigsten Datenblattwerte ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:12em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:7em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | Beispiel&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Drain Source Voltage &amp;lt;br&amp;gt;(Breakdown) || V(BR)_DSS &amp;lt;br&amp;gt; V_DS || 75V || Maximale Spannungsfestigkeit des Bauteiles zwischen Drain und Source&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Drain Current  || I_D(on)   || 55A @125°C  || Maximaler Dauerstrom bei 125°C Gehäusetemperatur &lt;br /&gt;
|-&lt;br /&gt;
| Pulsed Drain Current || ID_pulse &amp;lt;br&amp;gt; I_CD(on) || 240A || Maximaler Pulsstrom (Achtung die zulässige Zeitdauer des Pulses kann nur über die maximale Junctiontemperatur ermittelt werden)&lt;br /&gt;
|-&lt;br /&gt;
| Repetetive Avalanche Energy || t_sc ||  280mJ || Maximale Energie, welche beim Avalanche Durchbruch bei Überschreiten der maximalen Drain-Source Spannung im MOSFET bei z.&amp;amp;nbsp;B. 1% Puls/Pausen Verhältnis regelmäßig auftreten darf, ohne den FET zu schädigen&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R_DS_ON ||  0,01Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;25°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source&amp;lt;br&amp;gt;ON Resistance || R_DS_on ||  0,021Ω || Widerstand des eingeschalteten FETs bei &#039;&#039;&#039;175°C&#039;&#039;&#039;, V_GS = 10V und ID = 30A&lt;br /&gt;
|-&lt;br /&gt;
| Thermal Resistance&amp;lt;br&amp;gt;(junction-case) ||  R_th_JC ||  0,8K/W || Thermischer Widerstand im Transistor vom eigentlichen Chip im Inneren (junction) bis zur Rückseite des Transistorgehäuses (case)&lt;br /&gt;
|-&lt;br /&gt;
| Gate-Source&amp;lt;br&amp;gt;Threshold Voltage ||  V_GS(th) || 2,0-4,5V || Gatespannung, ab welcher der Transistor minimal leitend wird (I_D typisch 100-200µA), große Toleranz, typisch 1:2 zwischen Minimum und Maximum&lt;br /&gt;
|-&lt;br /&gt;
| Turn-on Delay ||  t_d(on) || 40ns ||  Verzögerung zwischen dem Einschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Rise Time ||  t_r || 200ns ||  Anstiegszeit des Transistorstromes am Drain&lt;br /&gt;
|-&lt;br /&gt;
| Turn-off Delay || t_d(off) || 120ns ||  Verzögerung zwischen Abschalten am Gate bis zur Reaktion im Drainstrom&lt;br /&gt;
|-&lt;br /&gt;
| Fall Time ||  t_f  || 60ns || Abfallzeit des Transistorstromes am Drain  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die oben genannten Zeiten gelten ausschließlich unter den angegebenen Messbedingungen (Gatewiderstand, Treiberspannung, sowie einer &#039;&#039;&#039;FET-Teperatur von 25°C!&#039;&#039;&#039;) und müssen für die eigene Anwendung ggf. neu berechnet werden. Meist wird man sie eher messen, weil die Rechung zu aufwändig und bisweilen unmöglich ist. &lt;br /&gt;
&lt;br /&gt;
==== Gate-Source Threshold Voltage ====&lt;br /&gt;
Gerade bei der &#039;&#039;&#039;Gate-Source Threshold Voltage &amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt;&#039;&#039;&#039; gibt es hier immer wieder Verwirrung. Sie gibt an, ab welcher Spannung der MOSFET anfängt, minimal leitfähig zu werden. Diese Spannung ist technologisch bedingt auch heute noch einer starken Toleranz unterworfen, typischerweise hat der Bereich  bei dem der FET zu leiten beginnt eine Spreizung von etwa 1:2 zwischen Minimum und Maximum. Welche Spannung man nun wirklich anlegen muss, um den gewünschten &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt; zu erreichen kann der Tabelle im jeweiligen Datenblatt entnommen werden. Dabei unbedingt die angegebene Gatespannung beachten, nur dieser Wert ist garantiert!.&lt;br /&gt;
&lt;br /&gt;
[[bild: IRLZ34N_R_DS_ON.png | thumb | 800px | R_DS_ON im Datenblatt des IRLZ34N]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Die Kurvenschar von &amp;lt;math&amp;gt;I_D&amp;lt;/math&amp;gt; über &amp;lt;math&amp;gt;U_{DS}&amp;lt;/math&amp;gt; in Abhängigkeit von &amp;lt;math&amp;gt;U_{GS}&amp;lt;/math&amp;gt; stellt immer nur typische Werte dar, keine garantierten Extremwerte (engl. worst case). Als Standardwerte kann man 10-15V für einen Standardtypen und ca. 3-5V für einen Logic Level MOSFET (LL-FET) ansetzen. Kleinsignal-FETs leiten schon ab ca 1V. Bei Ansteuerung mit 5V benötigt man also einen Typen, der &#039;&#039;&#039;sicher&#039;&#039;&#039; bei 5V voll durchgesteuert ist, z.B. IRLZ34N. Bei 3,3V ist er bereits nicht mehr zuverlässig nutzbar. Es gibt auch Typen mit noch geringerer Spannung für Vollaussteuerung. Wer einen BUZ11 (&amp;lt;math&amp;gt;V_{GS}(th)&amp;lt;/math&amp;gt; 4V max.) mit 5V ansteuert riskiert ein Abfackeln des MOSFETs, denn je nach Toleranz kann er bereit ganz gut aufgesteuert sein oder auch nicht. Das soll in den nachfolgenden Diagrammen dargestellt werden. Zunächst die Transferkennlinie. Sie gibt an, wieviel Drainstrom in Abhängigkeit der Gatespannung fließen kann, wobei die Drainspannung konstant ist, hier im Beispiel 25V. Ein typischer BUZ11 (mittlere, schwarze Kurve) fängt bei 3V zu leiten an und erreicht bei 6V am Gate ca. 17A. Erwischt man nun ein kritisches Exemplar, das erst bei 4V zu leiten anfängt (rechte, rote Kurve um 1V parallel verschoben), so kann dieser bei 6V nur 8A leiten, für 17A braucht er 7V. Der günstige Fall, daß der BUZ11 schon bei 2V anfängt zu leiten sieht man in der linken, roten Kurve.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UGS_ID.png | thumb | 800px | Transferkennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
Wenn man die drei Fälle (min, typ, max) mit 5V ansteuert, erhält man die Arbeitspunkte 1-3 mit einem maximal schaltbaren Drainstrom von 2,5, 8 und 17A. Diese kann man in das Ausgangskennlinienfeld übertragen. Wir nehmen max. 1V U_DS an. Es ergeben sie die gleichen Ströme. Der maximale Drainstrom weicht um -5,5/+9A vom typischen Fall ab. Erhöht man nun die Gate-Source Spannung auf 10V (Arbeitspunkt 4), schwankt der maximal schaltbare Drainstrom nur noch um -2/+2A, außerdem liegt er mit 30A deutlich höher. Der BUZ11 ist somit sicher durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
[[bild: BUZ11_UDS_ID_UGS.png | thumb | 800px | Ausgangskennlinie des BUZ11]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
==== Parasitäre Diode ====&lt;br /&gt;
&lt;br /&gt;
Der Schwerpunkt in der FET-Entwicklung liegt auf den geringst-möglichen &amp;lt;math&amp;gt;R_{DS}(on)&amp;lt;/math&amp;gt;,  die Diode entsteht auf Grund des Herstellungsprozesses, und wird nur nachrangig verbessert, da viele Optimierungsversuche auch einen Einfluss auf wichtige Kennwerte des FETs hatten und haben.&lt;br /&gt;
Daher muss sorgfältig geprüft werden, ob die Schaltgeschwindigkeit, die Recovery-Time und die damit verbundenen Verluste sowie die dabei erzeugte unerwünschte EMV-Abstahlung tolerierbar ist, oder nicht.&lt;br /&gt;
Hier hilft es oft eine optimierte Diode / Schottky-Diode zum FET parallel zu schalten. Ganz ausblenden läßt sich die parasitäre Diode jedoch nicht, jedoch kann man den Anteil des Stromes beeinflussen, der über die intere Diode fliest.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039; Parasitäre Diode des FETs  &#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:10em&amp;quot; | &#039;&#039;&#039;Parameter&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Symbol&#039;&#039;&#039;&lt;br /&gt;
! style=&amp;quot;width:6em&amp;quot; | &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Erklärung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Continuous Current  ||  I_S || 75A || Maximaler Dauerstrom der parasitären Diode, meist identisch zum maximalen Dauerstrom des MOSFETs&lt;br /&gt;
|-&lt;br /&gt;
| Forward Voltage ||  V_SD || 1,3V || Spannungsfall an der parasitären Diode &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Time ||  t_rr || 120ns || Zeit, welche die Elektronen brauchen um aus der leitenden Diode vollständig abzufließen. Während dieser Zeit fließt der Strom in &#039;&#039;&#039;Rückwärtsrichtung&#039;&#039;&#039; durch die Diode und erzeugt relativ viel Verlustleistung. &lt;br /&gt;
|-&lt;br /&gt;
| Reverse Recovery Charge ||  Q_rr || 60nC || Ladungsmenge, die während t_rr rückwärts durch die Diode fließt.  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Haupttypen und Gatespannungslevel ==&lt;br /&gt;
&lt;br /&gt;
===Unterschied N-Kanal / P-Kanal FET===&lt;br /&gt;
&lt;br /&gt;
Im Schaltsymbol werden die MOSFET-Typen meist durch die Pfeilrichtung in der Mitte des Symbols (eigentlich &amp;quot;Bulk&amp;quot;) vom oder zum Gate unterschieden.  Zeigt der Pfeil zum Gate hin, handelt es sich um einen N-Kanal-FET, zeigt der Pfeil vom Gate weg um einen P-Kanal FET.&lt;br /&gt;
&lt;br /&gt;
Der große Vorteil des N-Kanal FETs (Elektronenleitung) ist, daß er immer niederohmiger ist, als ein gleich großer P-Kanal FET (Löcherleitung). Daher sind P-Kanal Typen bei vergleichbaren Werten auch immer größer = teuerer da weniger Chips auf einem Wafer Platz haben.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET muss die Gatespannung positiv gegenüber Source sein. Dabei wird der FET dann leitend, wenn die sogenannte &amp;quot;threshold voltage&amp;quot; (Schwellenspannung) erreicht wird. Eine typische Anwendung ist z.&amp;amp;nbsp;B. ein &#039;&#039;&#039;Low-Side Schalter&#039;&#039;&#039;: Source an GND, Drain an die Last, Ansteuerung des N-Kanal FETs mit 12V gleichbedeutend mit 12V ÜBER den Source = GND Potential.&lt;br /&gt;
 &lt;br /&gt;
Beim P-Kanal FET als HS-Schalter muss die Gatespannung negativer=niedriger als das Sourcepotential sein.Beispiel.&lt;br /&gt;
Beispiel:  &lt;br /&gt;
Lastspannung = 400V d.h. Source an 400V, Last zwischen Drain und GND, Ansteuerung des P-Kanal FETs mit 388V, also 12V UNTER dem Sourcepotential.&lt;br /&gt;
&lt;br /&gt;
Beim N-Kanal FET als HS-Schalter muss die Gatespannung positver=höher als das Sourcepotential sein.&lt;br /&gt;
Beispiel:&lt;br /&gt;
Lastspannung = 400V d.h. Drain an 400V, die Last zwischen Source und GND, Ansteuerung des N-Kanal FETs mit 412V, also 12V ÜBER dem Sourcepotential.&lt;br /&gt;
In diesem Fall ist aber eine zusätzliche Spannungsquelle erforderlich, denn der FET wird mit einer Spannung über der Lastspannung eingeschaltet. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weblinks&#039;&#039;&#039;&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-1-of-2/?utm_source=eetimes&amp;amp;utm_medium=networksearch A primer on high-side FET load switches (Part 1 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products, Micrel, Inc., 5/3/2007 4:14 PM EDT, www.eetimes.com&lt;br /&gt;
* [https://www.eetimes.com/a-primer-on-high-side-fet-load-switches-part-2-of-2/ A primer on high-side FET load switches (Part 2 of 2)], Qi Deng, Senior Product Marketing Manager, Mixed-Signal Products. Micrel, Inc., 5/7/2007 1:36 PM EDT, www.eetimes.com&lt;br /&gt;
* [http://www.vishay.com/docs/70611/70611.pdf AN804 P-Channel MOSFETs, the Best Choice for High-Side Switching (PDF)] von Vishay Siliconix&lt;br /&gt;
&lt;br /&gt;
===Unterschied Logic-Level / &amp;quot;Normal&amp;quot;-Level===&lt;br /&gt;
&lt;br /&gt;
Den meisten FETs ist gemein, daß sie mit einer Spannung von 10..15V angesteuert werden müssen, um den minimalen Einschaltwiderstand zu erreichen. Diese FETs lassen sich nicht ohne weiteres mit einem CMOS-Pegel von 5V ansteuern. Es gibt jedoch für diesen Anwendungsfall sogenannte &amp;quot;Logic Level&amp;quot; (LL) FETs, die schon bei einer Gatespannung von etwa 4,5V voll durchgesteuert sind. Einige Kleinsignal-FETs sind schon ab ca. 1,2V voll durchgesteuert.&lt;br /&gt;
&lt;br /&gt;
== Beispiel zur Bauteiledimensionierung ==&lt;br /&gt;
&lt;br /&gt;
=== Spannungsfestigkeit ===&lt;br /&gt;
&lt;br /&gt;
Die höchste vorkommende Betriebsspannung + Abschaltüberspannung soll kleiner als ca. 80% der Spannungsfestigkeit des Bauteiles sein. &lt;br /&gt;
&lt;br /&gt;
Achtung: Zwischen dem je nach Anwendungsfall erforderlichen Pufferkondensator und dem FET wird es immer eine parasitäre Induktivität geben.&lt;br /&gt;
Abhängig von Schaltgeschwindigkeit und Induktivität wird im Schaltmoment eine mehr oder weniger große Übrspannungsspitze produziert. Dieser Peak&lt;br /&gt;
addiert sich auf die aktuelle Versorgungsspannung.&lt;br /&gt;
&lt;br /&gt;
Überschlagsrechnung als Beispiel:&lt;br /&gt;
* Schaltgeschwindigkeit:  dI/dt = -100A/µs (= Abschalten von 5A innerhalb 50ns),&lt;br /&gt;
* Induktivität:   L = 1µH (~ 1 m loses, ungebündeltes Kabel)&lt;br /&gt;
* dU=-L*dI/dt = -1µH * (-100A / 1µs) = 100V&lt;br /&gt;
&lt;br /&gt;
Dies bedeutet, daß an der &amp;quot;Induktivität&amp;quot; zwischen Transistor und Kondensator - Aufgrund von Selbstinduktion im Schaltmoment - ein Überspannungspuls von ca. 100V entsteht, der auf die Betriebsspannung aufzuschlagen ist, also lieber die Leitung kürzer machen, und - sofern möglich - nicht ganz so schnell schalten.&lt;br /&gt;
&lt;br /&gt;
=== Stromtragfähigkeit ===&lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt ist eine Stromtragfähigkeit bei 25°C, und meist noch bei einer höheren Temperatur z.B. 125°C, 150°C oder 175°C Kühlfahnentemperatur angegeben. Dieser Wert ist als ERSTE Entscheidungsgrundlage ausreichend, aber aus der theoretisch abführbaren Verlustleistung errechnet, und&lt;br /&gt;
* dient zum qualitativen Vergleich von Transistoren bezüglich ihres R_ds(on) und ihres Wärmewiderstands.&lt;br /&gt;
* ist für die Dimensionierung einer Schaltung nur als Richtwert zu interpretieren. &lt;br /&gt;
* ist ohne Schaltverluste genannt, und daher nur für einen Schaltbetrieb von wenigen Hz gültig. Außerdem wird ein annähernd idealer Kühlkörper unterstellt, der trotz der Verlustleistung das Gehäuse des Transistors auf der angegebenen Temperatur halten kann.&lt;br /&gt;
* entbindet einen nicht davon den Kopf einzuschalten... siehe die nachfolgenden Zeilen.&lt;br /&gt;
* Liegt der Strom für den die Schaltung entwickelt wird mit ca. 10..20% Abstand unter dem Datenblattwert von 125°C ist dieses Bauteil vermutlich verwendbar (siehe Detailberechnungen unten !).   &lt;br /&gt;
* Ist der benötigte Strom im Bereich oder größer als der zulässige bei 125°C sollte entweder ein anderer Typ eingesetzt oder mehrere FETs &#039;&#039;des gleichen Typs&#039;&#039; parallelgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
== Verlustleistung ==&lt;br /&gt;
&lt;br /&gt;
Hier wird eine Näherung für eine getaktete Anwendung betrachtet. In einem Transistor treten sowohl beim Ein- und Ausschalten, als auch während der Einschaltphase Verluste im Bauteil auf. Diese Verluste führen zu einer Bauteilerwärmung. Die dabei entstehende Temperatur darf die maximal zulässige Bauteiletemperatur nie überschreiten. Bei den ersten Projekten ist zu empfehlen eine berechnete Chiptemperatur von ca. 125°C nicht zu überschreiten. Fast alle aktuell verfügbaren FETs nennen im Datenblatt eine Temperatur von 175°C als ihre maximale Chiptemperatur.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:32em&amp;quot; &lt;br /&gt;
|+ &#039;&#039;&#039;Beispiel&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Symbol&lt;br /&gt;
! style=&amp;quot;width:8em&amp;quot; | Wert&lt;br /&gt;
|-&lt;br /&gt;
| Betriebsspannung || U&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 70 V&lt;br /&gt;
|-&lt;br /&gt;
| Nennstrom || I&amp;lt;sub&amp;gt;N&amp;lt;/sub&amp;gt; || 30 A&lt;br /&gt;
|-&lt;br /&gt;
| Drain-Source Widerstand bei&amp;lt;br&amp;gt;Chiptemperatur: 125°C&amp;lt;br&amp;gt; Gatespannung: 10V || R&amp;lt;sub&amp;gt;DS&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt;&amp;lt;/sub&amp;gt; || 17 mΩ&lt;br /&gt;
|-&lt;br /&gt;
| Pulsbreite || t&amp;lt;sub&amp;gt;on&amp;lt;/sub&amp;gt; || 150 µs&lt;br /&gt;
|-&lt;br /&gt;
| Schaltfrequenz || ƒ&amp;lt;sub&amp;gt;schalt&amp;lt;/sub&amp;gt; || 5 kHz&amp;lt;br&amp;gt;T = 200µs&lt;br /&gt;
|-&lt;br /&gt;
| Einschaltzeit (risetime) || t&amp;lt;sub&amp;gt;r&amp;lt;/sub&amp;gt; || 500 ns&lt;br /&gt;
|-&lt;br /&gt;
| Ausschaltzeit (falltime) || t&amp;lt;sub&amp;gt;ƒ&amp;lt;/sub&amp;gt; || 800 ns &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Leitend-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Während der FET bei [[PWM]]-Ansteuerung eingeschaltet ist, erzeugt er Verlustleistung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
P_\text{ON}&lt;br /&gt;
 = {I_\mathrm{N}}^2 \cdot R_\mathrm{DS_\mathrm{ON}} \cdot \frac{t_\mathrm{ON}}{T}&lt;br /&gt;
 = 30^2A^2 \cdot 17m\Omega  \cdot \frac{150\mu s}{200\mu s} = 11{,}5W&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schalt-Verluste ====&lt;br /&gt;
&lt;br /&gt;
Vereinfachter Ansatz.&lt;br /&gt;
&lt;br /&gt;
Einschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_r}&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_r}{T} \\&lt;br /&gt;
&amp;amp;= \tfrac14 \cdot 70V \cdot 30A \cdot \frac{500ns}{200\mu s}=1{,}3W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_\mathrm{SW_f} &lt;br /&gt;
&amp;amp;=\tfrac14 \cdot U_N \cdot I_N \cdot \frac{t_f}{T}\\&lt;br /&gt;
&amp;amp;=\tfrac14 \cdot 70V \cdot 30A \cdot \frac{800ns}{200\mu s}=2{,}1W&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ und genauer kann man rechnen, wenn die Ein- Ausschaltenergie im Datenblatt angegeben ist. Aber Achtung! Die  Randbedingungen unter denen die genannte Energie ermittelt wurde, müssen genau so zutreffen.&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_f} = f_{schalt} \cdot E_{ON}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
P_{SW_r} = f_{schalt} \cdot E_{OFF}&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Gesamtverlustleistung beträgt also in etwa 15W.&lt;br /&gt;
&lt;br /&gt;
Damit muß ein entsprechender [[Kühlkörper]] ausgelegt und die Chiptemperatur berechnet werden. z.&amp;amp;nbsp;B.:&lt;br /&gt;
* Kühlkörper mit einem R_th von 0,2K/W&lt;br /&gt;
* max. Umgebungstemperatur +60°C&lt;br /&gt;
* R_th &amp;quot;junction-case&amp;quot; des FETs 0,8K/W&lt;br /&gt;
* R_th der Wärmeleitfolie zwischen FET und Kühlkörper ca. 2,0K/W&lt;br /&gt;
* R_th gesamt: 3,0K/W &amp;lt;br&amp;gt;&lt;br /&gt;
* Bei einer Verlustleistung von 18W und einer Umgebungstemperatur von 60°C hat der Chip eine Temperatur von ca. 18W * 3,0K/W +60°C = 114°C. ==&amp;gt; o.k.!&lt;br /&gt;
&lt;br /&gt;
Unter Berücksichtigung der Tatsache, daß hier viele Vereinfachungen vorgenommen, und die Art der Last nicht beachtet wurde ist es sinnvoll, einen gewissen Sicherheitsabstand zu den zulässigen Maximalwerten einzuhalten. Daher ist es empfehlenswert, die Chiptemperatur auf ca. 125°C zu beschränken. &lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist hier die parasitäre Diode im FET nicht berücksichtigt.&lt;br /&gt;
Wenn während der &amp;quot;off&amp;quot; Zeit ein Strom über die Diode fließt (Reverse recovery current oder Freilaufstrom), muß die dadurch &#039;&#039;&#039;zusätzlich&#039;&#039;&#039; entstehende Verlustleistung in die obige Berechnung der maximalen Chiptemperatur mit einfließen.&lt;br /&gt;
&lt;br /&gt;
==Treiberleistung==&lt;br /&gt;
&lt;br /&gt;
Auch wenn der MOSFET ein spannungsgesteuertes Bauelement ist, muss trotzdem bei jedem Einschalten und bei jedem Ausschalten die Gatekapazität umgeladen werden. Bei älteren Leistungs-FET - oder bei einem schlechten Design (!) - muss sogar teilweise mit negativer Spannung am Gate gearbeitet werden, um eine vollständige Sperrung zu erreichen.&lt;br /&gt;
Diese Umladung muss möglichst schnell erfolgen, um die Verluste im FET während der Umschaltphase zu minimieren. Dazu findet ein [[Mosfet-Übersicht#MOSFET-Treiber|MOSFET-Treiber]] Verwendung. Hier eine detaillierte Beschreibung zum [[Treiber]].&lt;br /&gt;
&lt;br /&gt;
Da die Gatekapazität nicht direkt im Datenblatt enthalten ist kann man sich mit der Eingangskapazität Ciss behelfen. Im Arbeitspunkt ist die Gatekapazität ungefähr 5x größer als der im Datenblatt für Ciss angegebene Wert. &lt;br /&gt;
Daher berechnet sich die Treiberleistung wie folgt: &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{Treiber} = C \cdot U^2 \cdot f = 5 \cdot C_\text{íss} \cdot U_\text{Gate}^2 \cdot f_\text{schalt}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1.Beispiel, kleine MOSFET-Steuerung mit niedriger Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 4{,}8\,\text{nF} \cdot 15\,\text{V}^2 \cdot 10\,\text{kHz} = 54\,\text{mW}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2.Beispiel, sehr große MOSFET-Steuerung für Induktionsheizung mit sehr hoher Leistung und Frequenz.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;P_\text{treiber} = 5 \cdot 24\,\text{nF} \cdot 15\,\text{V}^2 \cdot 250\,\text{kHz} = 6{,}75\,\text{W}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung, so ein MOSFET-Treiber hat auch einen Eigenverbrauch, der leicht zwischen 0,5 und 1 W liegen kann.&lt;br /&gt;
&lt;br /&gt;
Bei niedrigen PWM-Frequenzen kann man Logic Level MOSFETs auch direkt per CMOS-Ausgang ansteuern, z.B. mit einem [[AVR]], wie in diesem [http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag] zu sehen ist.&lt;br /&gt;
&lt;br /&gt;
== Low- und High-Side ==&lt;br /&gt;
&lt;br /&gt;
Definition LS- und HS:&lt;br /&gt;
 &lt;br /&gt;
;Low-Side Schalter: Der FET schaltet eine Last gegen GND - auch als LS-Schalter bezeichnet.&lt;br /&gt;
;High-Side Schalter: Der FET schaltet eine Last an die Versorgungsspannung – auch als HS-Schalter bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Anregungen oder Fragen auch gerne per Email an [http://www.mikrocontroller.net/user/show/powerfreak Powerfreak]. Dieser Artikel kann dadurch regelmäßig erweitert und ggf. durch ein FAQ ergänzt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== SOA Diagramm ==&lt;br /&gt;
&lt;br /&gt;
SOA-Diagramm (engl. &#039;&#039;&#039;S&#039;&#039;&#039;afe &#039;&#039;&#039;O&#039;&#039;&#039;perating &#039;&#039;&#039;A&#039;&#039;&#039;rea, sicherer Arbeitsbereich) beschreibt die zulässige Verlustleistung eines Transistors in Anhängigkeit des Drainstroms (I_D), der Drain-Source Spannung (U_DS) und der Pulsbreite. Als Beispiel sei hier der BUZ 11 genannt. Im nachfolgenden Diagramm ist das SOA-Diagramm dargestellt. Wie ist es zu verstehen? Zunächst gibt es eine Grenze auf der linken Seite, die schräge, dunkelblaue Line. Diese wird durch den minimalen R_DS_ON festgelegt, hier wirkt der MOSFET wie ein ohmscher Widerstand. Mehr Strom kann bei einer bestimmten Spannung nicht fließen. Die zweite Grenzlinie ist ganz rechts die pinkfarbene Linie, sie stellt die maximale Sperrspannung des MOSFET dar. Die dritte Grenze ist der maximal zulässige Drainstrom, hier im Beispiel 120A, dargestellt durch die gelbe Linie. Die maximale Spannung zwischen Drain und Source sowie der Drainstrom sind abhängig von der Pulsbreite, mit welcher der MOSFET betrieben wird. Bei nur 2,5µs Pulsbreite (Rechteckimpuls) müssen die beiden Parameter sich innerhalb der Fläche bewegen, welche durch die dunkelblaue, gelbe und die pinkfarbene Line begrenzt wird. Im Extremfall dürfen 50V anliegen und 120A fließen, das sind satte 6kW Pulsleistung! Werden die Pulse breiter, so sinken die zulässigen Ströme und Spannungen, bei 1ms (dunkelblaue Linie bis zur braunen Linie, dann zur pinkfarbenen Linie) sind maximal noch 50V und 7A zulässig, also nur noch 350W. Die letzte Linie stellt den Fall für Gleichstrom (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent), also Dauerbelastung dar, hier sind bei 50V maximal 1,5A zulässig, was einer Dauerverlustleistung von 75W entspricht. MOSFETs, welche nur für Schaltbetrieb und nicht für [[#Linearbetrieb von MOSFETs | Linearbetrieb]] geeignet sind, haben keine Kennlinie für DC. Im normalen Schaltbetrieb liegt der Arbeitspunkt auf der linken Grenzlinie R_DS_ON_MIN. Nur im Linearbetrieb liegt der Arbeitspunkt innerhalb der Fläche, welche durch die Außenlinien begrenzt wird.&lt;br /&gt;
&lt;br /&gt;
[[bild: SOA-BUZ11.png | thumb | 300px| SOA-Diagramm]]&lt;br /&gt;
&lt;br /&gt;
Bei der Anwendung des Diagramms gilt es einiges zu beachten. Die Pulsleistungen sind nur zulässig, wenn der MOSFET vorher kalt ist, sprich ca. 25°C Sperrschichttemperatur hat. War er vorher schon heiß, reduziert sich die zulässige Belastung deutlich. Ebenso dürfen die Pulse nicht zu schnell wiederholt werden, denn dann ist der MOSFET noch vom vorherigen Puls aufgeheizt. Im Fall von DC sind 75W Verlustleistung auch eher ein theoretischer Wert, welcher real nur schwer erreicht werden kann, wenn der MOSFET auf einem sehr großen [[Kühlkörper]] optimal montiert ist. Praktisch liegen die erreichbaren Werte eher bei der Hälfte.&lt;br /&gt;
&lt;br /&gt;
(Anm. Eigentlich müsste für die R_DS_ON Grenzlinie R = U / I der minimale R_DS_ON rauskommen, hier ~40mOhm, es kommen aber ~80mOhm raus. Die Ursache dafür ist unklar, möglicherweise liegt hier ein Sicherheitsfaktor zu grunde).&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Linearbetrieb von MOSFETs ==&lt;br /&gt;
&lt;br /&gt;
Der Großteil der Anwendungen nutzt MOSFETs als Schalter, d.h. der MOSFET ist entweder voll gesperrt oder voll durchgesteuert. Dafür gelten auch all die Hinweise in diesem Artikel. In bestimmten Anwendungen werden MOSFETs aber auch im Linearbetrieb eingesetzt, z.B in linearen Endstufen für Audio, Video, elektronischen Lasten und Stromquellen. Hier muss man einiges beachten. Ein verbreiteter Irrtum besteht darin zu glauben, MOSFETs könne man im Linearbetrieb einfach parallel schalten, weil der positive Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; eine Symmetrierung bewirkt, ähnlich den Emitterwiderständen bei parallelgeschalteten Bipolartransistoren. Das ist &#039;&#039;ausschließlich&#039;&#039; im Schaltbetrieb möglich, und daher falsch! Im Linearbetrieb spielt der Temperaturkoeffizient von &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; keine Rolle, weil der MOSFET selten bis nie komplett durchgesteuert ist. Eben darum ist beim Linearbetrieb der minimale &amp;lt;math&amp;gt;R_{DS(ON)}&amp;lt;/math&amp;gt; in den meisten Fällen unwichtig und man kann auch eher hochohmige, ältere MOSFETs verwenden, wie z.B. den BUZ11.&lt;br /&gt;
&lt;br /&gt;
Hier wirkt vielmehr der negative Temperaturkoeffizient (TK) der Thresholdspannung &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, vergleichbar dem negativen TK der Basis-Emitter-Spannung von Bipolartransistoren. D.h. mit steigender Temperatur und konstanter Gate-Source-Spannung steigt der Stromfluss der Drain-Source Strecke. In einer Parallelschaltung von MOSFETs würde dies bedeuten, dass der MOSFET mit dem geringfügig größeren Drainstrom (Fertigungstoleranzen) wärmer wird, was zu einem weiter steigenden Drainstrom und damit noch mehr Wärme führt. Damit ist die Schaltung thermisch instabil und würde zum Durchbrennen der MOSFETs führen, einer nach dem Anderen. &lt;br /&gt;
&lt;br /&gt;
Um das zu verhindern muss man relativ große Ausgleichswiderstände in die Source-Leitung der einzelnen MOSFETs schalten, um diese Drift zu kompensieren. Dadurch verschlechtert sich natürlich der Wirkungsgrad des Verstärkers. MOSFETs haben einen TK von typisch -5mV/K für &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt;, das ist mehr als das doppelte von Bipolartransistoren mit typisch -2mV/K, weshalb die Symmetrierungswiderstände mehr als doppelt so groß sein müssen. Weiterhin muss man beachten, dass die Toleranzen von &amp;lt;math&amp;gt;U_{GS(thr)}&amp;lt;/math&amp;gt; sehr groß sind, im Bereich von Volt! Das kann man sinnvoll nicht mehr mit Gatewiderständen symmetrieren, hier muss man die MOSFETs ausmessen und Gruppen mit geringen Toleranzen in einer Schaltung verwenden (engl. matching). &lt;br /&gt;
&lt;br /&gt;
Eine andere Möglichkeit ist die getrennte Ansteuerung der einzelnen MOSFETs, das wird oft in elektronischen Lasten bzw. [[Konstantstromquelle#Konstantstromquelle mit Operationsverstärker und Transistor | Konstantstromquellen]] gemacht. Hier treten keine zusätzlichen Verluste auf und der Mehraufwand in der Ansteuerung ist meist unkritisch.&lt;br /&gt;
&lt;br /&gt;
Weiterhin muss man beachten, dass viele der heutigen HochleistungsMOSFETs intern eine Parallelschaltung vieler kleiner MOSFET-Zellen (z.B. sogenannte Trench-FET) sind, und somit oft für den Linearbetrieb ungeeignet sind. Denn auch dort können einzelne Zellen überhitzen und durchbrennen (Hot Spot). Ob ein MOSFET für den Linearbetrieb tauglich ist steht manchmal im Datenblatt, oft aber eher nicht, eben weil die meisten MOSFETs als Schalter entwickelt und gebaut sind. Typische Vertreter für Linearbetrieb findet man in der [[MOSFET-Übersicht]]. Ein wichtiges Indiz für Linearbetrieb ist eine Kurve für DC im [[#SOA_Diagramm | SOA-Diagramm]]. Meist geht es dort nur bis 10ms, DC fehlt, eben weil DC (engl. &#039;&#039;&#039;D&#039;&#039;&#039;irect &#039;&#039;&#039;C&#039;&#039;&#039;urrent = Gleichstrom = Linearbetrieb) nicht zulässig ist. Manchmal hat der Hersteller auch &amp;quot;vergessen&amp;quot;, die Kennlinie für DC mit reinzuschreiben, wie z.B. bei [http://www.irf.com/product-info/hi-rel/alerts/fv5-p-09-01-A.pdf IRF], wie in diesem [http://www.mikrocontroller.net/topic/291760#3106758 Beitrag] zu erfahren ist.&lt;br /&gt;
Ein recht gutes Indiz dafür, ob ein FET für den Linearbetrieb taugt, ist die Vorwärtssteilheit. Diese kennzeichnet die Abhängigkeit des Drainstromes von der Ansteuerung am Gate als &amp;lt;math&amp;gt;S = \Delta i_d/\Delta u_{gs}&amp;lt;/math&amp;gt;. Moderne Trench-FET erreichen heute Steilheiten im dreistelligen Bereich und sind für Linearanwendungen völlig unbrauchbar. Zum Vergleich: Der BUZ11 kommt mit gerade einmal 4 bis 5 Siemens daher.&lt;br /&gt;
&lt;br /&gt;
In diesem Beitrag wird die DC-Linie im SOA-Diagramm noch genauer erklärt: [http://www.mikrocontroller.net/topic/319961#3473567 Re: MOSFET Linearbetrieb möglich?]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Leistungselektronik]]&lt;br /&gt;
* [[Mosfet-Übersicht]]&lt;br /&gt;
* [[IGBT]]&lt;br /&gt;
* [[TRIAC]]&lt;br /&gt;
* [[Kühlkörper]] &lt;br /&gt;
* [[Zwischenkreiskapazität]]&lt;br /&gt;
* [[Treiber]]&lt;br /&gt;
* [[Snippets#Wie_schlie.C3.9Fe_ich_einen_MOSFET_an_einen_Mikrocontroller_an.3F|Wie schließe ich einen Mosfet an einen Mikrocontroller an?]]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/H-Br%C3%BCcken_%C3%9Cbersicht Übersicht H-Brücken]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/168218#1609684 Forumsbeitrag]: MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/186785#new Forumsbeitrag]: nochmal MOSFETs im Linearbetrieb&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/319961#3473567 Forumsbeitrag]: sehr ausführlicher Forumsbeitrag über MOSFETs im Linearbetrieb. Berücksichtigt auch den Spirito-Effekt.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/143324#new Forumsbeitrag]: Über eine elektronische Last, sehr lang&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/246449#2519459 Forumsbeitrag]: Logic Level MOSFETs direkt mit einem [[AVR]] treiben.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787855 Forumsbeitrag]: MOSFETs im Linearbetrieb, Laborerfahrungen&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/267254#2787945 Forumsbeitrag]: MOSFETs für Linearbetrieb&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/269642?goto=2820617#2820617 Forumsbeitrag]: Verpol- und Überspannungsschutz mit MOSFETs&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/425414#4981611 Forumsbeitrag]: Linearbetrieb von MOSFETs&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.elektronikinfo.de/strom/feldeffekttransistoren.htm Feldeffekttransistoren bei elektronikinfo.de]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0207011.htm FET im ELKO]&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0510161.htm MOSFET im ELKO]&lt;br /&gt;
* [http://www.sprut.de/electronic/switch/nkanal/nkanal.html MOSFET bei sprut.de]&lt;br /&gt;
* [http://sound.westhost.com/articles/hexfet.htm#51 MOSFETs in Audioendstufen, engl.]&lt;br /&gt;
* [http://irf.custhelp.com/cgi-bin/irf.cfg/php/enduser/std_adp.php?p_faqid=214&amp;amp;p_created=1019728945&amp;amp;p_sid=pt9ITiCj&amp;amp;p_accessibility=0&amp;amp;p_redirect=&amp;amp;p_lva=&amp;amp;p_sp=cF9zcmNoPTEmcF9zb3J0X2J5PSZwX2dyaWRzb3J0PSZwX3Jvd19jbnQ9MTQsMTQmcF9wcm9kcz0mcF9jYXRzPSZwX3B2PSZwX2N2PSZwX3BhZ2U9MSZwX3NlYXJjaF90ZXh0PWxpbmVhcg**&amp;amp;p_li=&amp;amp;p_topview=1 FAQ Answer ID 214 bei IRF zum Linearbetrieb]&lt;br /&gt;
* [http://www.nxp.com/documents/application_note/AN11158.pdf AN11158 - Understanding power MOSFET data sheet parameters] von NXP (PDF)&lt;br /&gt;
* [https://assets.nexperia.com/documents/technical-note/TN00008.pdf TN00008 - Power MOSFET frequently asked questions and answers] von nexperia (PDF)&lt;br /&gt;
* [http://www.infineon.com/dgdl/Infineon+-+Application+Note+-+PowerMOSFETs+-+OptiMOS%E2%84%A2+-+Linear+Mode+Operation+and+SOA+Power+MOSFETs.pdf?fileId=db3a30433e30e4bf013e3646e9381200 AN: Linear Mode Operation andSafe Operating Diagram of Power-MOSFETs] von Infineon (PDF)&lt;br /&gt;
* [http://www.ixys.com/Documents/Articles/Article_Linear_Power_MOSFETs.pdf MOSFETs Withstand Stress of Linear-Mode Operation] Neuentwickelte MOSFETs für Linearbetrieb (PDF)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]] [[Kategorie:Leistungselektronik]]&lt;/div&gt;</summary>
		<author><name>Heha</name></author>
	</entry>
</feed>