<?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=Mkleemann</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=Mkleemann"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/Mkleemann"/>
	<updated>2026-04-14T15:03:48Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Power_Management&amp;diff=67368</id>
		<title>AVR-Tutorial: Power Management</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Power_Management&amp;diff=67368"/>
		<updated>2012-07-18T12:42:24Z</updated>

		<summary type="html">&lt;p&gt;Mkleemann: /* Praxis */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;ACHTUNG!&#039;&#039;&#039;&lt;br /&gt;
Dieser Artikel befindet sich noch im Aufbau!&lt;br /&gt;
&lt;br /&gt;
Vorallem in batteriebetriebenen Systemen spielt die Leistungsaufnahme eine wichtige Rolle, d.h. sie soll so niedrig wie möglich gehalten werden um eine lange Laufzeit zu erreichen. Den sparsamen Umgang mit der verfügbaren el. Ladung nennt man &#039;&#039;&#039;Power Management&#039;&#039;&#039; (dt. Energiesparen).&lt;br /&gt;
&lt;br /&gt;
Im Rahmen des Power Managements stehen uns beispielsweise die Sleep-Modi zur Verfügung, mit denen wir bestimmte Module zeitweise deaktivieren können. Andere garnicht genutzte Module können wir durch entsprechende Konfiguration (z.B. in den Fuses) auch komplett deaktivieren.&lt;br /&gt;
&lt;br /&gt;
== Theorie ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Sleep Modi ===&lt;br /&gt;
&lt;br /&gt;
Welche Sleep-Modi es gibt, hängt vom verwendeten µC ab, dieser Artikel nimmt jedoch Bezug auf den ATmega32. Um einen der verfügbaren Sleep-Modi des ATmega32 zu betreten müssen folgende Schritte ausgeführt werden&lt;br /&gt;
&lt;br /&gt;
# Das SE-Bit im MCUCR-Register wird auf 1 gesetzt&lt;br /&gt;
# Die SMx-Bits im MCUCR-Register je nach gewünschtem Modus setzen&lt;br /&gt;
# Der SLEEP-Befehl wird ausgeführt&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller geht dann sofort in den SLEEP-Modus, d.h. noch vor eventuell anstehenden Interrupts, und wacht erst wieder auf wenn ein Signal eines geeigneten Moduls (je nach Modus) ihn aufweckt.&lt;br /&gt;
&lt;br /&gt;
Die Arbeit wird dann mit der ersten Anweisung hinter dem SLEEP-Befehl wieder aufgenommen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ MCUCR - MCU Control Register&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
! Bit&lt;br /&gt;
| style=&amp;quot;width:12%&amp;quot; | 7&lt;br /&gt;
| style=&amp;quot;width:12%&amp;quot; | 6&lt;br /&gt;
| style=&amp;quot;width:12%&amp;quot; | 5&lt;br /&gt;
| style=&amp;quot;width:12%&amp;quot; | 4&lt;br /&gt;
| style=&amp;quot;width:12%; background-color:#c0c0c0&amp;quot; | 3&lt;br /&gt;
| style=&amp;quot;width:12%; background-color:#c0c0c0&amp;quot; | 2&lt;br /&gt;
| style=&amp;quot;width:12%; background-color:#c0c0c0&amp;quot; | 1&lt;br /&gt;
| style=&amp;quot;width:12%; background-color:#c0c0c0&amp;quot; | 0&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
! Bezeichnung&lt;br /&gt;
| SE&lt;br /&gt;
| SM2&lt;br /&gt;
| SM1&lt;br /&gt;
| SM0&lt;br /&gt;
| style=&amp;quot;background-color:#c0c0c0&amp;quot; | ISC11&lt;br /&gt;
| style=&amp;quot;background-color:#c0c0c0&amp;quot; | ISC10&lt;br /&gt;
| style=&amp;quot;background-color:#c0c0c0&amp;quot; | ISC01&lt;br /&gt;
| style=&amp;quot;background-color:#c0c0c0&amp;quot; | ISC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
;Bit 7 - SE: Sleep Enable&lt;br /&gt;
:Mit diesem Bit wird bestimmt ob der Sleep-Befehl ausgeführt wird (1) oder nicht (0).&lt;br /&gt;
&lt;br /&gt;
;Bit 6..4 - SM2..0: Sleep Mode Select&lt;br /&gt;
:Mit diesen drei Bits wird der gewünschte Sleep-Modus gewählt&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! SM2&lt;br /&gt;
! SM1&lt;br /&gt;
! SM0&lt;br /&gt;
! style=&amp;quot;text-align:left&amp;quot; | Sleep Modus&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Idle&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | ADC Noise Reduction&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Power-down&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Power-save&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Reserved&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Reserved&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Standby&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Extended Standby&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt; Nur verfügbar mit externem Taktgeber&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Modi Übersicht====&lt;br /&gt;
&lt;br /&gt;
Generell ist der Modus zu wählen, der die meisten nicht benötigten Module abschaltet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!&lt;br /&gt;
! colspan=&amp;quot;5&amp;quot; | Aktive Takte &lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Aktive Oszillatoren&lt;br /&gt;
! colspan=&amp;quot;6&amp;quot; | Weckquellen&lt;br /&gt;
|- style=&amp;quot;background-color:#f0f0f0&amp;quot;&lt;br /&gt;
| Sleep Modus&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;CPU&amp;lt;/sub&amp;gt;&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;FLASH&amp;lt;/sub&amp;gt;&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;IO&amp;lt;/sub&amp;gt;&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;ADC&amp;lt;/sub&amp;gt;&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;ASY&amp;lt;/sub&amp;gt;&lt;br /&gt;
| Haupttaktgeber&lt;br /&gt;
| Timer Oszillator&lt;br /&gt;
| INT2&amp;lt;br /&amp;gt;INT1&amp;lt;br /&amp;gt;INT0&lt;br /&gt;
| TWI Address Match&lt;br /&gt;
| Timer2&lt;br /&gt;
| SPM/EEPROM Ready&lt;br /&gt;
| ADC&lt;br /&gt;
| Andere I/O&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Idle&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | ADC Noise Reduction&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| &lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Power-down&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Power-save&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Standby&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| x&lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Extended Standby&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt; Nur verfügbar bei externer Taktquelle&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt; Wenn AS2-Bit in ASSR-Register gesetzt&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt; Nur INT2 oder Level Interrupt INT1 und INT0&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Manuelles Deaktivieren ===&lt;br /&gt;
Einzelne Module können auch manuell deaktviert werden um Strom zu sparen, das bietet sich vorallem an wenn bestimmte Module im gegebenen Projekt generell nicht benötigt werden und damit deaktiviert werden können.&lt;br /&gt;
&lt;br /&gt;
==== Analog to Digital Converter ====&lt;br /&gt;
todo...&lt;br /&gt;
&lt;br /&gt;
==== Analog Comparator ====&lt;br /&gt;
Der Analogkomparator ist standardmäßig aktiviert. Um ihn zu deaktivieren, muss man ADC (Bit 7) im Register ACSR setzen.&lt;br /&gt;
&lt;br /&gt;
==== Brown-Out Detector ====&lt;br /&gt;
Der Brown-Out Detector lässt sich entweder durch das BODEN-Bit in den Fuses oder mit entsprechenden Befehlen aktivieren oder deaktivieren. Das Fuse-Bit ist standardmäßig gesetzt (Achtung: Umgekehrte Logik!) und der BOD damit deaktiviert.&lt;br /&gt;
&lt;br /&gt;
==== Watchdog ====&lt;br /&gt;
Auch der Watchdog-Timer lässt sich in den Fuses standardmäßig aktivieren/deaktivieren, hier über das WDTON-Bit.&lt;br /&gt;
Natürlich geht auch das softwareseitig [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Watchdog]&lt;br /&gt;
&lt;br /&gt;
== Praxis ==&lt;br /&gt;
&lt;br /&gt;
=== Assembler ===&lt;br /&gt;
&#039;&#039;&#039;TODO:&#039;&#039;&#039; ASM-Quellcode Beispiele&lt;br /&gt;
&lt;br /&gt;
=== C ===&lt;br /&gt;
&lt;br /&gt;
Ein simples Testprogramm, um mit sleep modi im AVR zu spielen. Es funktioniert sehr gut mit dem ATmega8 und ist auch auf andere AVRs portierbar. Teilweise sind weitere Modi verfügbar.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, daß die Interruptroutine für den &amp;quot;Weckruf&amp;quot; vorhanden sein muss. Es müssen nicht zwingend Aktionen in ihr durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/* ATmega8 with internal 4Mhz clock (6cycle + 64ms) */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   DDRC |= (1 &amp;lt;&amp;lt; PC2) | (1 &amp;lt;&amp;lt; PC1); // leds for testing&lt;br /&gt;
&lt;br /&gt;
   DDRD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD2); // INT0: input...&lt;br /&gt;
   PORTD |= (1 &amp;lt;&amp;lt; PD2); // ...with pullup.&lt;br /&gt;
&lt;br /&gt;
   // level interrupt INT0 (low level)&lt;br /&gt;
   MCUCR &amp;amp;= ~((1 &amp;lt;&amp;lt; ISC01) | (1 &amp;lt;&amp;lt; ISC00));&lt;br /&gt;
&lt;br /&gt;
   // infinite main loop&lt;br /&gt;
   while (1)&lt;br /&gt;
   {&lt;br /&gt;
      // trigger leds for testing&lt;br /&gt;
      PORTC ^= (1 &amp;lt;&amp;lt; PC1);&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      PORTC ^= (1 &amp;lt;&amp;lt; PC1);&lt;br /&gt;
&lt;br /&gt;
      // enable external interrupt&lt;br /&gt;
      GICR |= (1 &amp;lt;&amp;lt; INT0);&lt;br /&gt;
&lt;br /&gt;
      // set sleep mode&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
&lt;br /&gt;
      // sleep_mode() has a possible race condition&lt;br /&gt;
      sleep_enable();&lt;br /&gt;
      sei();&lt;br /&gt;
      sleep_cpu();&lt;br /&gt;
      sleep_disable();&lt;br /&gt;
&lt;br /&gt;
      // waking up...&lt;br /&gt;
      // disable external interrupt here, in case the external low pulse is too long&lt;br /&gt;
      GICR &amp;amp;= ~(1 &amp;lt;&amp;lt; INT0);&lt;br /&gt;
&lt;br /&gt;
      // disable all interrupts&lt;br /&gt;
      cli();&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)&lt;br /&gt;
{&lt;br /&gt;
   // ISR might be empty, but is necessary nonetheless&lt;br /&gt;
   PORTC ^= (1 &amp;lt;&amp;lt; PC2); // debugging&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellen ==&lt;br /&gt;
[http://www.atmel.com/dyn/resources/prod_documents/doc2503.pdf ATMEL AVR ATmega32 Datenblatt]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
{{Navigation_zurückhoch|&lt;br /&gt;
zurücktext=Power Management|&lt;br /&gt;
zurücklink=AVR-Tutorial: Power Management|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie: AVR-Tutorial|Power Management]]&lt;/div&gt;</summary>
		<author><name>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=63062</id>
		<title>AVR-Tutorial: Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=63062"/>
		<updated>2012-01-03T16:46:47Z</updated>

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

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

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

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

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

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

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

		<summary type="html">&lt;p&gt;Mkleemann: Kategoriensortierung&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Allgemeines ===&lt;br /&gt;
&lt;br /&gt;
Speziell bei den &#039;&#039;&#039;16-Bit-Timern&#039;&#039;&#039; und auch beim &#039;&#039;&#039;ADC&#039;&#039;&#039; 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;&#039;Lesezugriff&#039;&#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;&#039;Schreibzugriff&#039;&#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 beim &#039;&#039;&#039;Lesezugriff: Erst Low-Byte, dann High-Byte&#039;&#039;&#039; und für den &#039;&#039;&#039;Schreibzugriff: Erst High-Byte, dann Low-Byte&#039;&#039;&#039;.&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. &#039;&#039;&#039;16-Bit-Zugriffe sind generell nicht atomar!&#039;&#039;&#039; Wenn mit &#039;&#039;&#039;Interrupts&#039;&#039;&#039; 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 &#039;&#039;&#039;ADC-Datenregister ADCH/ADCL&#039;&#039;&#039; 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. Dadaurch 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;
&#039;&#039;&#039;Nach einem Zugriff auf ADCL muss grundsätzlich ADCH gelesen werden!&#039;&#039;&#039;&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;
=== Zugriffe in C ===&lt;br /&gt;
&lt;br /&gt;
In beiden Fällen (also sowohl bei den Timern als auch beim ADC) werden von &#039;&#039;&#039;C-Compilern&#039;&#039;&#039; 16-Bit-Pseudo-Register zur Verfügung gestellt (z.&amp;amp;nbsp;B. TCNT1H/TCNT1L &amp;amp;rarr; TCNT1, ADCH/ADCL &amp;amp;rarr; ADC bzw. ADCW), bei deren Verwendung der Compiler automatisch die richtige Zugriffsreihenfolge regelt. &#039;&#039;&#039;In C-Programmen sollten grundsätzlich diese 16-Bit-Register verwendet werden&#039;&#039;&#039;. 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;
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;
&lt;br /&gt;
&lt;br /&gt;
=== Makros ===&lt;br /&gt;
&lt;br /&gt;
Um den Zugriff auf diese 16Bit Register zu vereinfachen&lt;br /&gt;
hast ATMEL in der Application Note AVR072 [http://www.atmel.com/dyn/resources/prod_documents/doc1493.pdf] folgende Makros&lt;br /&gt;
zur freien Verwendung vorgeschlagen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AVR Assembler Macros&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.macro outw&lt;br /&gt;
  cli&lt;br /&gt;
  out @0, @1&lt;br /&gt;
  out @0-1, @2&lt;br /&gt;
  sei&lt;br /&gt;
.endmacro&lt;br /&gt;
&lt;br /&gt;
.macro inw&lt;br /&gt;
  cli&lt;br /&gt;
  in @1, @2-1&lt;br /&gt;
  in @0, @2&lt;br /&gt;
  sei&lt;br /&gt;
.endmacro&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Usage&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.include “8515def.inc”&lt;br /&gt;
&lt;br /&gt;
inw r17, r16, TCNT1H ; Reads the counter value (high, low, adr)&lt;br /&gt;
outw TCNT1H, r17, r16 ; Writes the counter value (adr, high, low)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IAR C MACROS&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;ina90.h&amp;gt;&lt;br /&gt;
#define outw( ADDRESS, VAL )\&lt;br /&gt;
  {\&lt;br /&gt;
  _CLI();&lt;br /&gt;
  ADDRESS = VAL;\&lt;br /&gt;
  _SEI();\&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
#define inw( ADDRESS, VAL )\&lt;br /&gt;
  {\&lt;br /&gt;
  _CLI();&lt;br /&gt;
  VAL = ADDRESS;\&lt;br /&gt;
  _SEI();\&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Usage&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;io8515.h&amp;gt;&lt;br /&gt;
inw( TCNT1, i ) ;/* Reads the counter value */&lt;br /&gt;
outw( TCNT1, i ) ;/* Writes the counter value */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie: AVR-Tutorial|AVR 16-Bit-Register]]&lt;/div&gt;</summary>
		<author><name>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Arithmetik&amp;diff=62986</id>
		<title>AVR Arithmetik</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Arithmetik&amp;diff=62986"/>
		<updated>2012-01-02T20:27:33Z</updated>

		<summary type="html">&lt;p&gt;Mkleemann: /* Weblinks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel beschäftigt sich mit 16- und 32-Bit Arithmetik auf [[AVR]]-Controllern. Für detailierte Ausführungen zur [[AVR-Tutorial: Arithmetik8|8-Bit Arithmetik auf AVR]] gibt es eine Seite im [[AVR-Tutorial]].&lt;br /&gt;
&lt;br /&gt;
== Glossar ==&lt;br /&gt;
&lt;br /&gt;
MSB: Most Significant Byte, Höchstwertiges Byte&lt;br /&gt;
&lt;br /&gt;
LSB: Least Significant Byte, Niedrigstwertiges Byte&lt;br /&gt;
&lt;br /&gt;
== Vergleich ==&lt;br /&gt;
&lt;br /&gt;
=== 16 Bit ===&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ;&lt;br /&gt;
    ;              LSB  MSB         LSB  MSB&lt;br /&gt;
    ; Vergleiche &amp;lt; r16, r17 &amp;gt; mit &amp;lt; r20, r21 &amp;gt;&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
    cp    r16, r20&lt;br /&gt;
    cpc   r17, r21&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 32 Bit ===&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ;&lt;br /&gt;
    ;              LSB            MSB         LSB            MSB&lt;br /&gt;
    ; Vergleiche &amp;lt; r16, r17, r18, r19 &amp;gt; mit &amp;lt; r20, r21, r22, r23 &amp;gt;&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
    cp    r16, r20&lt;br /&gt;
    cpc   r17, r21&lt;br /&gt;
    cpc   r18, r22&lt;br /&gt;
    cpc   r19, r23&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Addition==&lt;br /&gt;
===16 Bit + 16 Bit===&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ;&lt;br /&gt;
    ; &amp;lt; r16, r17 &amp;gt; = &amp;lt; r16, r17 &amp;gt; + &amp;lt; r20, r21 &amp;gt;&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
    add   r16, r20&lt;br /&gt;
    adc   r17, r21&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===32 Bit + 32 Bit===&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ;&lt;br /&gt;
    ; &amp;lt; r16, r17, r18, r19 &amp;gt; = &amp;lt; r16, r17, r18, r19 &amp;gt; + &amp;lt; r20, r21, r22, r23 &amp;gt;&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
    add   r16, r20&lt;br /&gt;
    adc   r17, r21&lt;br /&gt;
    adc   r18, r22&lt;br /&gt;
    adc   r19, r23&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Subtraktion==&lt;br /&gt;
===16 Bit - 16 Bit===&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ;&lt;br /&gt;
    ; &amp;lt; r16, r17 &amp;gt; = &amp;lt; r16, r17 &amp;gt; - &amp;lt; r20, r21 &amp;gt;&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
    sub   r16, r20&lt;br /&gt;
    sbc   r17, r21&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===32 Bit - 32 Bit===&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ;&lt;br /&gt;
    ; &amp;lt; r16, r17, r18, r19 &amp;gt; = &amp;lt; r16, r17, r18, r19 &amp;gt; - &amp;lt; r20, r21, r22, r23 &amp;gt;&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
    sub   r16, r20&lt;br /&gt;
    sbc   r17, r21&lt;br /&gt;
    sbc   r18, r22&lt;br /&gt;
    sbc   r19, r23&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Division==&lt;br /&gt;
===32 Bit / 32 Bit===&lt;br /&gt;
&lt;br /&gt;
Ergebnis gerundet, und mit Restbildung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.def	a0	= r16&lt;br /&gt;
.def	a1	= r17&lt;br /&gt;
.def	a2	= r18&lt;br /&gt;
.def	a3	= r19&lt;br /&gt;
&lt;br /&gt;
.def	b0	= r20&lt;br /&gt;
.def	b1	= r21&lt;br /&gt;
.def	b2	= r22&lt;br /&gt;
.def	b3	= r23&lt;br /&gt;
&lt;br /&gt;
.def	t0	= r24&lt;br /&gt;
.def	t1	= r25&lt;br /&gt;
.def	t2	= r26&lt;br /&gt;
.def	t3	= r27&lt;br /&gt;
.def	t4	= r28&lt;br /&gt;
&lt;br /&gt;
;************************************************************************&lt;br /&gt;
;*                                                                      *&lt;br /&gt;
;*                      unsigned rounded division 32 bit                *&lt;br /&gt;
;*                                                                      *&lt;br /&gt;
;************************************************************************&lt;br /&gt;
&lt;br /&gt;
urdiv32:&lt;br /&gt;
	mov	t0, b0		;T = B&lt;br /&gt;
	mov	t1, b1&lt;br /&gt;
	mov	t2, b2&lt;br /&gt;
	mov	t3, b3&lt;br /&gt;
	lsr	t3		;B / 2&lt;br /&gt;
	ror	t2&lt;br /&gt;
	ror	t1&lt;br /&gt;
	ror	t0&lt;br /&gt;
	add	a0, t0		;A = A + B / 2&lt;br /&gt;
	adc	a1, t1&lt;br /&gt;
	adc	a2, t2&lt;br /&gt;
	adc	a3, t3&lt;br /&gt;
&lt;br /&gt;
;************************************************************************&lt;br /&gt;
;*                                                                      *&lt;br /&gt;
;*                      unsigned division 32 bit                        *&lt;br /&gt;
;*                                                                      *&lt;br /&gt;
;************************************************************************&lt;br /&gt;
&lt;br /&gt;
; a3..0 = a3..0 / b3..0&lt;br /&gt;
; b3..0 = remainde&lt;br /&gt;
&lt;br /&gt;
;cycle: max 684&lt;br /&gt;
&lt;br /&gt;
udiv32:&lt;br /&gt;
	clr	t0&lt;br /&gt;
	clr	t1&lt;br /&gt;
	clr	t2&lt;br /&gt;
	clr	t3&lt;br /&gt;
	ldi	t4, 32&lt;br /&gt;
udi1:	lsl	a0&lt;br /&gt;
	rol	a1&lt;br /&gt;
	rol	a2&lt;br /&gt;
	rol	a3&lt;br /&gt;
	rol	t0&lt;br /&gt;
	rol	t1&lt;br /&gt;
	rol	t2&lt;br /&gt;
	rol	t3&lt;br /&gt;
	cp	t0, b0&lt;br /&gt;
	cpc	t1, b1&lt;br /&gt;
	cpc	t2, b2&lt;br /&gt;
	cpc	t3, b3&lt;br /&gt;
	brcs	udi2&lt;br /&gt;
	sub	t0, b0&lt;br /&gt;
	sbc	t1, b1&lt;br /&gt;
	sbc	t2, b2&lt;br /&gt;
	sbc	t3, b3&lt;br /&gt;
	inc	a0&lt;br /&gt;
udi2:	dec	t4&lt;br /&gt;
	brne	udi1&lt;br /&gt;
	mov	b0, t0&lt;br /&gt;
	mov	b1, t1&lt;br /&gt;
	mov	b2, t2&lt;br /&gt;
	mov	b3, t3&lt;br /&gt;
	ret&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Version, die je nach konkreten Zahlen Rechenzeit einspart&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.def	a0	= r16&lt;br /&gt;
.def	a1	= r17&lt;br /&gt;
.def	a2	= r18&lt;br /&gt;
.def	a3	= r19&lt;br /&gt;
&lt;br /&gt;
.def	b0	= r20&lt;br /&gt;
.def	b1	= r21&lt;br /&gt;
.def	b2	= r22&lt;br /&gt;
.def	b3	= r23&lt;br /&gt;
&lt;br /&gt;
.def	t0	= r24&lt;br /&gt;
.def	t1	= r25&lt;br /&gt;
.def	t2	= r26&lt;br /&gt;
.def	t3	= r27&lt;br /&gt;
&lt;br /&gt;
;************************************************************************&lt;br /&gt;
;*                                                                      *&lt;br /&gt;
;*                      unsigned rounded division 32 bit                *&lt;br /&gt;
;*                                                                      *&lt;br /&gt;
;************************************************************************&lt;br /&gt;
&lt;br /&gt;
urdiv32:&lt;br /&gt;
	mov	t0, b0		;T = B&lt;br /&gt;
	mov	t1, b1&lt;br /&gt;
	mov	t2, b2&lt;br /&gt;
	mov	t3, b3&lt;br /&gt;
	lsr	t3		;B / 2&lt;br /&gt;
	ror	t2&lt;br /&gt;
	ror	t1&lt;br /&gt;
	ror	t0&lt;br /&gt;
	add	a0, t0		;A = A + B / 2&lt;br /&gt;
	adc	a1, t1&lt;br /&gt;
	adc	a2, t2&lt;br /&gt;
	adc	a3, t3&lt;br /&gt;
&lt;br /&gt;
;************************************************************************&lt;br /&gt;
;*                                                                      *&lt;br /&gt;
;*                      unsigned division 32 bit                        *&lt;br /&gt;
;*                                                                      *&lt;br /&gt;
;************************************************************************&lt;br /&gt;
;cycle: max 431 (63%) (684)&lt;br /&gt;
&lt;br /&gt;
udiv32:&lt;br /&gt;
	clr	t1&lt;br /&gt;
	tst	b3&lt;br /&gt;
	breq	udi10&lt;br /&gt;
	ldi	t0, 8&lt;br /&gt;
udi1:	lsl	a0&lt;br /&gt;
	rol	a1&lt;br /&gt;
	rol	a2&lt;br /&gt;
	rol	a3&lt;br /&gt;
	rol	t1&lt;br /&gt;
	cp	a1, b0&lt;br /&gt;
	cpc	a2, b1&lt;br /&gt;
	cpc	a3, b2&lt;br /&gt;
	cpc	t1, b3&lt;br /&gt;
	brcs	udi2&lt;br /&gt;
	sub	a1, b0&lt;br /&gt;
	sbc	a2, b1&lt;br /&gt;
	sbc	a3, b2&lt;br /&gt;
	sbc	t1, b3&lt;br /&gt;
	inc	a0&lt;br /&gt;
udi2:	dec	t0&lt;br /&gt;
	brne	udi1&lt;br /&gt;
	mov	b0, a1&lt;br /&gt;
	clr	a1&lt;br /&gt;
	mov	b1, a2&lt;br /&gt;
	clr	a2&lt;br /&gt;
	mov	b2, a3&lt;br /&gt;
	clr	a3&lt;br /&gt;
	mov	b3, t1&lt;br /&gt;
	ret&lt;br /&gt;
&lt;br /&gt;
udi10:	tst	b2&lt;br /&gt;
	breq	udi20&lt;br /&gt;
	ldi	t0, 16&lt;br /&gt;
udi11:	lsl	a0&lt;br /&gt;
	rol	a1&lt;br /&gt;
	rol	a2&lt;br /&gt;
	rol	a3&lt;br /&gt;
	rol	t1&lt;br /&gt;
	brcs	udi12&lt;br /&gt;
	cp	a2, b0&lt;br /&gt;
	cpc	a3, b1&lt;br /&gt;
	cpc	t1, b2&lt;br /&gt;
	brcs	udi13&lt;br /&gt;
udi12:	sub	a2, b0&lt;br /&gt;
	sbc	a3, b1&lt;br /&gt;
	sbc	t1, b2&lt;br /&gt;
	inc	a0&lt;br /&gt;
udi13:	dec	t0&lt;br /&gt;
	brne	udi11&lt;br /&gt;
	mov	b0, a2&lt;br /&gt;
	clr	a2&lt;br /&gt;
	mov	b1, a3&lt;br /&gt;
	clr	a3&lt;br /&gt;
	mov	b2, t1&lt;br /&gt;
	ret&lt;br /&gt;
&lt;br /&gt;
udi20:	tst	b1&lt;br /&gt;
	breq	udi30&lt;br /&gt;
	ldi	t0, 24&lt;br /&gt;
udi21:	lsl	a0&lt;br /&gt;
	rol	a1&lt;br /&gt;
	rol	a2&lt;br /&gt;
	rol	a3&lt;br /&gt;
	rol	t1&lt;br /&gt;
	brcs	udi22&lt;br /&gt;
	cp	a3, b0&lt;br /&gt;
	cpc	t1, b1&lt;br /&gt;
	brcs	udi23&lt;br /&gt;
udi22:	sub	a3, b0&lt;br /&gt;
	sbc	t1, b1&lt;br /&gt;
	inc	a0&lt;br /&gt;
udi23:	dec	t0&lt;br /&gt;
	brne	udi21&lt;br /&gt;
	mov	b0, a3&lt;br /&gt;
	clr	a3&lt;br /&gt;
	mov	b1, t1&lt;br /&gt;
	ret&lt;br /&gt;
&lt;br /&gt;
udi30:	ldi	t0, 32&lt;br /&gt;
udi31:	lsl	a0&lt;br /&gt;
	rol	a1&lt;br /&gt;
	rol	a2&lt;br /&gt;
	rol	a3&lt;br /&gt;
	rol	t1&lt;br /&gt;
	brcs	udi32&lt;br /&gt;
	cp	t1, b0&lt;br /&gt;
	brcs	udi33&lt;br /&gt;
udi32:	sub	t1, b0&lt;br /&gt;
	inc	a0&lt;br /&gt;
udi33:	dec	t0&lt;br /&gt;
	brne	udi31&lt;br /&gt;
	mov	b0, t1			;store remainder&lt;br /&gt;
	ret&lt;br /&gt;
;------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Multiplikation==&lt;br /&gt;
===32 Bit * 16 Bit===&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.def	a0	= r16&lt;br /&gt;
.def	a1	= r17&lt;br /&gt;
.def	a2	= r18&lt;br /&gt;
.def	a3	= r19&lt;br /&gt;
&lt;br /&gt;
.def	b0	= r20&lt;br /&gt;
.def	b1	= r21&lt;br /&gt;
.def	b2	= r22&lt;br /&gt;
.def	b3	= r23&lt;br /&gt;
&lt;br /&gt;
.def	t0	= r24&lt;br /&gt;
.def	t1	= r25&lt;br /&gt;
.def	t2	= r26&lt;br /&gt;
.def	t3	= r27&lt;br /&gt;
.def	i0	= r28&lt;br /&gt;
&lt;br /&gt;
;************************************************************************&lt;br /&gt;
;*                                                                      *&lt;br /&gt;
;*                      unsigned multiplication 32 bit                  *&lt;br /&gt;
;*                                                                      *&lt;br /&gt;
;************************************************************************&lt;br /&gt;
;cycle: max 245&lt;br /&gt;
&lt;br /&gt;
umul32:&lt;br /&gt;
	cpi	a3, 0&lt;br /&gt;
	cpc	a3, a2&lt;br /&gt;
	breq	_umu1		;one operand must be below 65536&lt;br /&gt;
	mov	t0, a0		; swap A &amp;lt;-&amp;gt; B&lt;br /&gt;
	mov	a0, b0&lt;br /&gt;
	mov	b0, t0&lt;br /&gt;
	mov	t0, a1&lt;br /&gt;
	mov	a1, b1&lt;br /&gt;
	mov	b1, t0&lt;br /&gt;
	mov	b2, a2&lt;br /&gt;
	mov	b3, a3&lt;br /&gt;
	clr	a2&lt;br /&gt;
	clr	a3&lt;br /&gt;
;				a3,2,1,0 = a1,0 * b3,2,1,0&lt;br /&gt;
_umu1:	ldi	i0, 16&lt;br /&gt;
	clr	t0&lt;br /&gt;
	clr	t1&lt;br /&gt;
	ror	a1&lt;br /&gt;
	ror	a0&lt;br /&gt;
_umu2:	brcc	_umu3&lt;br /&gt;
	add	a2, b0&lt;br /&gt;
	adc	a3, b1&lt;br /&gt;
	adc	t0, b2&lt;br /&gt;
	adc	t1, b3&lt;br /&gt;
_umu3:	ror	t1&lt;br /&gt;
	ror	t0&lt;br /&gt;
	ror	a3&lt;br /&gt;
	ror	a2&lt;br /&gt;
	ror	a1&lt;br /&gt;
	ror	a0&lt;br /&gt;
	dec	i0&lt;br /&gt;
	brne	_umu2&lt;br /&gt;
	ret&lt;br /&gt;
;------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===24 Bit * 24 Bit===&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;**********************************************************************************************&lt;br /&gt;
;&lt;br /&gt;
;  muls24x24_48:&lt;br /&gt;
;  Signed Multiply von 2 24Bit breiten Zahlen mit 48Bit ergebnis&lt;br /&gt;
;&lt;br /&gt;
;  x                       = a           * b&lt;br /&gt;
;  R21:R20:R19:R18:R17:R16 = R27:R26:R25 * R24:R23:R22&lt;br /&gt;
;  hi                  lo    hi      lo    hi      lo&lt;br /&gt;
;&lt;br /&gt;
;**********************************************************************************************&lt;br /&gt;
&lt;br /&gt;
muls24x24_48:&lt;br /&gt;
    clr    r2          ;Zero Register&lt;br /&gt;
    muls  r27,r24        ; (1) signed Multiply a(MSB) * b(MSB)&lt;br /&gt;
    mov    r21,r1&lt;br /&gt;
    mov    r20,r0&lt;br /&gt;
&lt;br /&gt;
    mul    r26,r23        ; (2) unsigned&lt;br /&gt;
    mov    r18,r0&lt;br /&gt;
    mov    r19,r1&lt;br /&gt;
&lt;br /&gt;
    mul    r25,r22        ; (3) unsigned multiply a(LSB) * b(LSB)&lt;br /&gt;
    mov    r17,r1&lt;br /&gt;
    mov    r16,r0&lt;br /&gt;
&lt;br /&gt;
    mul    r26,r22        ;(4) unsigned&lt;br /&gt;
    add    r17,r0&lt;br /&gt;
    adc    r18,r1&lt;br /&gt;
    adc    r19,r2&lt;br /&gt;
    adc    r20,r2&lt;br /&gt;
    adc    r21,r2&lt;br /&gt;
&lt;br /&gt;
    mul    r25,r23        ;(5) unsigned&lt;br /&gt;
    add    r17,r0&lt;br /&gt;
    adc    r18,r1&lt;br /&gt;
    adc    r19,r2&lt;br /&gt;
    adc    r20,r2&lt;br /&gt;
    adc    r21,r2&lt;br /&gt;
&lt;br /&gt;
    push  r16&lt;br /&gt;
    push  r17&lt;br /&gt;
&lt;br /&gt;
    mov    r16,r27&lt;br /&gt;
    mulsu  r16,r22        ;(6) unsigned * signed&lt;br /&gt;
    sbc    r20,r2&lt;br /&gt;
    sbc    r21,r2&lt;br /&gt;
    add    r18,r0&lt;br /&gt;
    adc    r19,r1&lt;br /&gt;
    adc    r20,r2&lt;br /&gt;
    adc    r21,r2&lt;br /&gt;
&lt;br /&gt;
    mulsu  r16,r23        ;(7) unsigned * signed&lt;br /&gt;
    sbc    r21,r2&lt;br /&gt;
    add    r19,r0&lt;br /&gt;
    adc    r20,r1&lt;br /&gt;
    adc    r21,r2&lt;br /&gt;
&lt;br /&gt;
    mov    r16,r24&lt;br /&gt;
    mov    r17,r25&lt;br /&gt;
    mulsu  r16,r17        ;(8) unsigned * signed&lt;br /&gt;
    sbc    r20,r2&lt;br /&gt;
    sbc    r21,r2&lt;br /&gt;
    add    r18,r0&lt;br /&gt;
    adc    r19,r1&lt;br /&gt;
    adc    r20,r2&lt;br /&gt;
    adc    r21,r2&lt;br /&gt;
&lt;br /&gt;
    mov    r17,r26&lt;br /&gt;
    mulsu  r16,r17        ;(9) unsigned * signed&lt;br /&gt;
    sbc    r21,r2&lt;br /&gt;
    add    r19,r0&lt;br /&gt;
    adc    r20,r1&lt;br /&gt;
    adc    r21,r2&lt;br /&gt;
&lt;br /&gt;
    pop    r17&lt;br /&gt;
    pop    r16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Wurzel==&lt;br /&gt;
&lt;br /&gt;
=== avr-gcc Implementierung (32 Bit) ===&lt;br /&gt;
Quadratwurzel basierend auf einer Implementierung von Ruud v Gessel&amp;lt;ref&amp;gt;[http://members.chello.nl/j.beentjes3/Ruud/sqrt32avr.htm Ruud v Gessel: Quadratwurzeln]&amp;lt;/ref&amp;gt;, die zusammen mit avr-gcc verwendet werden kann. Je nach Algorithmus wird das  Ergebnis zum Nächsten gerundet oder abgerundet. Abrunden ist dann angesagt, wenn die Wurzel aus einer großen Eingabe wie &amp;lt;tt&amp;gt;0xffffffff&amp;lt;/tt&amp;gt; zu ziehen ist, da bei Aufrunden hier das Ergebnis zu 0 überläuft.&lt;br /&gt;
&lt;br /&gt;
Die maximale Ausführungszeit ist höchstens 310 Ticks (inclusive CALL+RET):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;sqrt32.h&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#ifndef SQRT32_H&lt;br /&gt;
#define SQRT32_H&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
extern uint16_t sqrt32_round (uint32_t);&lt;br /&gt;
extern uint16_t sqrt32_floor (uint32_t);&lt;br /&gt;
&lt;br /&gt;
#endif /* SQRT32_H */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
C-Module, welche die Wurzel benötigen, includen einfach diesen Header. Das Assembler-Modul wird assembliert und zum Projekt hinzugelinkt.&lt;br /&gt;
&lt;br /&gt;
==== Rundung zum Nächsten ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}}&lt;br /&gt;
|+ &#039;&#039;&#039;Ressourcen-Verbrauch, Rundung zum Nächsten&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|bgcolor=&amp;quot;#ddffdd&amp;quot;| &#039;&#039;&#039;Flash&#039;&#039;&#039;    || 82 Bytes&lt;br /&gt;
|-&lt;br /&gt;
|bgcolor=&amp;quot;#ddffdd&amp;quot;| &#039;&#039;&#039;RAM&#039;&#039;&#039;      || 2&amp;amp;ndash;3 Bytes dynamisch, 0 Bytes statisch&lt;br /&gt;
|-&lt;br /&gt;
|bgcolor=&amp;quot;#ddffdd&amp;quot;| &#039;&#039;&#039;Laufzeit&#039;&#039;&#039; || 265&amp;amp;ndash;310 Ticks (inclusive Zeiten für CALL+RET)&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;sqrt32.S&#039;&#039;&#039; (sqrt32_round)&lt;br /&gt;
{{Scrollbox|25em|&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------&lt;br /&gt;
; Fast and short 32 bits AVR sqrt routine, avr-gcc ABI compliant&lt;br /&gt;
; R25:R24 = SQRT (R25:R24:R23:R22) rounded to the &lt;br /&gt;
; nearest integer (0.5 rounds up)&lt;br /&gt;
; Destroys R18-R19,R22-R23,R26-R27&lt;br /&gt;
; Cycles incl call &amp;amp; ret = 265-310&lt;br /&gt;
; Stack incl call = 2-3&lt;br /&gt;
;-----------------------------------------------------------&lt;br /&gt;
.text&lt;br /&gt;
.global sqrt32_round&lt;br /&gt;
.type sqrt32_round, @function&lt;br /&gt;
&lt;br /&gt;
sqrt32_round:&lt;br /&gt;
    ldi   R19, 0xc0&lt;br /&gt;
    clr   R18          ; rotation mask in R19:R18&lt;br /&gt;
    ldi   R27, 0x40&lt;br /&gt;
    sub   R26, R26     ; developing sqrt in R27:R26, C=0&lt;br /&gt;
1:  brcs  2f           ; C --&amp;gt; Bit is always 1&lt;br /&gt;
    cp    R24, R26&lt;br /&gt;
    cpc   R25, R27     ; Does test value fit?&lt;br /&gt;
    brcs  3f           ; C --&amp;gt; nope, bit is 0&lt;br /&gt;
2:  sub   R24, R26&lt;br /&gt;
    sbc   R25, R27     ; Adjust argument for next bit&lt;br /&gt;
    or    R26, R18&lt;br /&gt;
    or    R27, R19     ; Set bit to 1&lt;br /&gt;
3:  lsr   R19&lt;br /&gt;
    ror   R18          ; Shift right mask, C --&amp;gt; end loop&lt;br /&gt;
    eor   R27, R19&lt;br /&gt;
    eor   R26, R18     ; Shift right only test bit in result&lt;br /&gt;
    rol   R22          ; Bit 0 only set if end of loop&lt;br /&gt;
    rol   R23&lt;br /&gt;
    rol   R24&lt;br /&gt;
    rol   R25          ; Shift left remaining argument (C used at 1:)&lt;br /&gt;
    sbrs  R22, 0       ; Skip if 15 bits developed&lt;br /&gt;
    rjmp  1b           ; Develop 15 bits of the sqrt&lt;br /&gt;
    brcs  4f           ; C--&amp;gt; Last bits always 1&lt;br /&gt;
    cp    R26, R24&lt;br /&gt;
    cpc   R27, R25     ; Test for last bit 1&lt;br /&gt;
    brcc  5f           ; NC --&amp;gt; bit is 0&lt;br /&gt;
4:  sbc   R23, R19     ; Subtract C (any value from 1 to 0x7f will do)&lt;br /&gt;
    sbc   R24, R26&lt;br /&gt;
    sbc   R25, R27     ; Update argument for test&lt;br /&gt;
    inc   R26          ; Last bit is 1&lt;br /&gt;
5:  lsl   R23          ; Only bit 7 matters&lt;br /&gt;
    rol   R24&lt;br /&gt;
    rol   R25          ; Remainder * 2 + C&lt;br /&gt;
    brcs  6f           ; C --&amp;gt; Always round up&lt;br /&gt;
    cp    R26, R24&lt;br /&gt;
    cpc   R27, R25     ; C decides rounding&lt;br /&gt;
6:  adc   R26, R19&lt;br /&gt;
    adc   R27, R19     ; Round up if C (R19=0)&lt;br /&gt;
    mov   R25, R27     ; return in R25:R24 for avr-gcc ABI compliance&lt;br /&gt;
    mov   R24, R26&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.size sqrt32_round, .-sqrt32_round&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==== Abrunden ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}}&lt;br /&gt;
|+ &#039;&#039;&#039;Ressourcen-Verbrauch, Abrunden&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|bgcolor=&amp;quot;#ddffdd&amp;quot;| &#039;&#039;&#039;Flash&#039;&#039;&#039;    || 60 Bytes&lt;br /&gt;
|-&lt;br /&gt;
|bgcolor=&amp;quot;#ddffdd&amp;quot;| &#039;&#039;&#039;RAM&#039;&#039;&#039;      || 2&amp;amp;ndash;3 Bytes dynamisch, 0 Bytes statisch&lt;br /&gt;
|-&lt;br /&gt;
|bgcolor=&amp;quot;#ddffdd&amp;quot;| &#039;&#039;&#039;Laufzeit&#039;&#039;&#039; || 260&amp;amp;ndash;300 Ticks (inclusive Zeiten für CALL+RET)&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;sqrt32.S&#039;&#039;&#039; (sqrt32_floor)&lt;br /&gt;
{{Scrollbox|25em|&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&lt;br /&gt;
;  Fast and short 32 bits AVR sqrt routine, avr-gcc ABI compliant&lt;br /&gt;
;  R25:R24 = SQRT (R25:R24:R23:R22) &lt;br /&gt;
;  rounded down to integer&lt;br /&gt;
;     Destroys R26,R27,R22,R23,R18,R19&lt;br /&gt;
;  Cycles incl call &amp;amp; ret = 260-300&lt;br /&gt;
;  Stack incl call = 2-3&lt;br /&gt;
.text&lt;br /&gt;
.global sqrt32_floor&lt;br /&gt;
.type sqrt32_floor, @function&lt;br /&gt;
&lt;br /&gt;
sqrt32_floor:&lt;br /&gt;
    ldi   R19, 0xc0&lt;br /&gt;
    clr   R18               ; rotation mask in R19:R18&lt;br /&gt;
    ldi   R27, 0x40&lt;br /&gt;
    sub   R26, R26          ; developing sqrt in R27:R26, C=0&lt;br /&gt;
1:  brcs  2f                ; C --&amp;gt; Bit is always 1&lt;br /&gt;
    cp    R24, R26&lt;br /&gt;
    cpc   R25, R27          ; Does test value fit?&lt;br /&gt;
    brcs  3f                ; C --&amp;gt; nope, bit is 0&lt;br /&gt;
2:  sub   R24, R26&lt;br /&gt;
    sbc   R25, R27          ; Adjust argument for next bit&lt;br /&gt;
    or    R26, R18&lt;br /&gt;
    or    R27, R19          ; Set bit to 1&lt;br /&gt;
3:  lsr   R19&lt;br /&gt;
    ror   R18               ; Shift right mask, C --&amp;gt; end loop&lt;br /&gt;
    eor   R27, R19&lt;br /&gt;
    eor   R26, R18          ; Shift right only test bit in result&lt;br /&gt;
    rol   R22               ; Bit 0 only set if end of loop&lt;br /&gt;
    rol   R23&lt;br /&gt;
    rol   R24&lt;br /&gt;
    rol   R25               ; Shift left remaining argument (C used at 1:)&lt;br /&gt;
    sbrs  R22, 0            ; Skip if 15 bits developed&lt;br /&gt;
    rjmp  1b                ; Develop 15 bits of the sqrt&lt;br /&gt;
&lt;br /&gt;
    brcs  4f                ; C--&amp;gt; Last bits always 1&lt;br /&gt;
    lsl   R23               ; Need bit 7 in C for cpc&lt;br /&gt;
    cpc   R26, R24&lt;br /&gt;
    cpc   R27, R25          ; After this C is last bit&lt;br /&gt;
&lt;br /&gt;
4:  adc   R26, R19          ; Round up if C (R19=0)&lt;br /&gt;
    mov   R25, R27          ; return in R25:R24 as for avr-gcc ABI&lt;br /&gt;
    mov   R24, R26&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.size sqrt32_floor, .-sqrt32_floor&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== avr-gcc Implementierung (16 Bit) ===&lt;br /&gt;
&lt;br /&gt;
Falls eine MUL-Instruktion vorhanden ist, benötigt eine 16-Bit Implementierung der Quadratwurzel nur eine handvoll Instruktionen und kann einigermassen bequem in Inline-Assembler geschrieben werden. Der Assembler-Teil besteht lediglich aus 9&amp;amp;nbsp;Instruktionen und dauert unabhängig vom Eingabewert 80&amp;amp;nbsp;Ticks.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;sqrt16_floor&#039;&#039;&#039; (Braucht MUL, Inline-Assembler und auf Größe optimiert)&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#if defined (__AVR_ENHANCED__)&lt;br /&gt;
&lt;br /&gt;
static inline uint8_t&lt;br /&gt;
sqrt16_floor (uint16_t q)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t res = 0;&lt;br /&gt;
    uint8_t mask = 1 &amp;lt;&amp;lt; 7;&lt;br /&gt;
    &lt;br /&gt;
    asm(&amp;quot;0:	add  %[res], %[mask]&amp;quot;   &amp;quot;\n&amp;quot;&lt;br /&gt;
        &amp;quot;	mul  %[res], %[res]&amp;quot;    &amp;quot;\n&amp;quot;&lt;br /&gt;
        &amp;quot;	cp   %A[q], R0&amp;quot;         &amp;quot;\n&amp;quot;&lt;br /&gt;
        &amp;quot;	cpc  %B[q], R1&amp;quot;         &amp;quot;\n&amp;quot;&lt;br /&gt;
        &amp;quot;	brsh 1f&amp;quot;                &amp;quot;\n&amp;quot;&lt;br /&gt;
        &amp;quot;	sub  %[res], %[mask]&amp;quot;   &amp;quot;\n&amp;quot;&lt;br /&gt;
        &amp;quot;1:	lsr  %[mask]&amp;quot;           &amp;quot;\n&amp;quot;&lt;br /&gt;
        &amp;quot;	brne 0b&amp;quot;                &amp;quot;\n&amp;quot;&lt;br /&gt;
        &amp;quot;	clr  __zero_reg__&amp;quot;&lt;br /&gt;
        : [res] &amp;quot;+r&amp;quot; (res), [mask] &amp;quot;+r&amp;quot; (mask)&lt;br /&gt;
        : [q] &amp;quot;r&amp;quot; (q));&lt;br /&gt;
        &lt;br /&gt;
    return res;&lt;br /&gt;
}&lt;br /&gt;
#endif // __AVR_ENHANCED__&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es handelt sich um eine größenoptimierte Version eines Algorithmus&#039; von Marko Surup, siehe &#039;&#039;[http://www.mikrocontroller.net/topic/231332#2338343 Forum: AVR: 16-Bit Quadratwurzel in 63 Takten]&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;C-Variante&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die C-Variante verbraucht auf einem ATmega etwa 25 Instruktionen und 120 Ticks inclusive Funktionsaufruf und Parameteraufbereitung (getestet mit avr-gcc 4.6 und auf Größe optimiert):&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t c_sqrt16 (uint16_t q)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t r, mask;&lt;br /&gt;
    uint8_t i = 8*sizeof (r) -1;&lt;br /&gt;
&lt;br /&gt;
    r = mask = 1 &amp;lt;&amp;lt; i;&lt;br /&gt;
    &lt;br /&gt;
    for (; i &amp;gt; 0; i--)&lt;br /&gt;
    {&lt;br /&gt;
        mask &amp;gt;&amp;gt;= 1;&lt;br /&gt;
        &lt;br /&gt;
        if (q &amp;lt; (uint16_t) r*r)&lt;br /&gt;
            r -= mask;&lt;br /&gt;
        else&lt;br /&gt;
            r += mask;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (q &amp;lt; (uint16_t) r*r)&lt;br /&gt;
        r -= 1;&lt;br /&gt;
    &lt;br /&gt;
    return r;&lt;br /&gt;
}&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Binär zu BCD - Umwandlung==&lt;br /&gt;
Zur Ausgabe einer Binärzahl auf ein Textdisplay oder zur seriellen ASCII-Übertragung an den PC ist diese Umwandlung nötig. Sie ist ähnlich aufwendig wie eine Division, zum Beispiel für eine 32-Bit-Zahl etwa 500-900 Taktzyklen und 10 Register.&lt;br /&gt;
Mehrere Verfahren werden angeboten:&lt;br /&gt;
=== Division /10===&lt;br /&gt;
Für eine Division pro Dezimalstelle ist nur ein Unterprogramm nötig. Der Divisionsrest, engl. remainder, bildet unmittelbar die BCD-codierte Dezimalziffer, von rechts nach links fortschreitend. Zur Ausgabe von links nach rechts kann man die Ziffern auf dem Stack zwischenlagern (LIFO-Register last-in first-out)&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
*[http://www.atmel.com/dyn/resources/prod_documents/doc0938.pdf AVR204: BCD Arithmetics, 8-Bit Binary to 2-digit BCD Conversion]&lt;br /&gt;
&lt;br /&gt;
===Division /10000, /1000, /100, /10===&lt;br /&gt;
ähnlich dem vorigen, aber die BCD-Ziffern erscheinen sofort von links nach rechts.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
*[http://users.i.com.ua/~birua/math32.asm Bin2BCD == 16-bit Binary to BCD conversion in der Macrolibrary Math32 von Andre Birua]&lt;br /&gt;
*[http://avr-asm.tripod.com/math32x.html oder auch hier zu finden]&lt;br /&gt;
&lt;br /&gt;
===Addition von $33===&lt;br /&gt;
Hier wird die Binärzahl, mit der höchstwertigen Stelle voran, von rechts in die Ergebnisregister geschoben. Nach jedem Schiebevorgang wird eine Korrekturrechnung ausgeführt. Für 32 Bit bilden 4 Eingabe- und 5 Ausgaberegister ein 72 Bit Schieberegister, das 32 mal links geschoben wird.&lt;br /&gt;
Durch die Korrekturrechnung wird die Binärzahl zur gepackten (2 Stellen pro Byte) BCD-Zahl &amp;quot;aufgebläht&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Eine Erklärung des Algorithmus soll hier versucht werden: &lt;br /&gt;
Um die niederwertigste 4 Bit Binärziffer in BCD umzuwandeln, ist für $0...$9 keine Änderung nötig, für $A...$F wird &amp;quot;6&amp;quot; addiert. Diese Addition wird ersetzt durch eine Addition von &amp;quot;3&amp;quot; und anschließendes Linksschieben. Das führt man zunächst für beide Halbbytes gleichzeitig mit &amp;quot;subi Reg,-$33&amp;quot; aus. Anschließend werden die beiden Additionen einzeln für den Fall &amp;quot;0...9&amp;quot; rückgängig gemacht. Die Bits 4 und 7 des Registers dient dabei als BCD-Carry-Flag (Halbbyte-Übertrag). Das normale C-Flag wird durch die Additionen nicht beeinflußt, es dient nur dem Schiebevorgang.&lt;br /&gt;
&lt;br /&gt;
Beispiele: &lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0938.pdf AVR204: BCD Arithmetics 16 Bit Binary to 5-digit BCD conversion]&lt;br /&gt;
* [http://avr-asm.tripod.com/math32x.html The AVR Assembler Site: 32 Bit Math]&lt;br /&gt;
* [http://www.ingelec.uns.edu.ar/dclac2558/BCD2BIN.PDF Der gleiche serielle Algorithmus für Xilinx CPLDs]&lt;br /&gt;
&lt;br /&gt;
===Tabelle===&lt;br /&gt;
Für kleine Zahlen kann die gesuchte BCD-Zahl auch aus einer Tabelle entnommen werden.&lt;br /&gt;
===Mischvarianten===&lt;br /&gt;
Eine Mischung mehrerer Verfahren wird z.&amp;amp;nbsp;B. von Andre Birua in der ersten Variante von &amp;quot;Bin4BCD&amp;quot; verwendet. Die oberen 16 Bit werden nach &amp;quot;Div/10000&amp;quot; vorbearbeitet und anschließend alle 32 Bit nach &amp;quot;Add $33&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Tutorial===&lt;br /&gt;
*[http://www.cs.uiowa.edu/~jones/bcd/decimal.html Part of  the Arithmetic Tutorial Collection by Douglas W. Jones] &lt;br /&gt;
enthält C-Code und PIC-Code für schnelle 16 Bit Wandlung, Hardware-Multiplizierer möglich - auch für FPGA interessant&lt;br /&gt;
*[http://www.mikrocontroller.net/articles/TTL74185 VHDL Nachbau des TTL 74185 6-Bit Binär-zu-BCD-Wandlers]&lt;br /&gt;
*[http://www.mikrocontroller.net/articles/Festkommaarithmetik Tutorial Festkommaarithmetik]&lt;br /&gt;
*[http://www.mikrocontroller.net/articles/AVR-Tutorial:_7-Segment-Anzeige  Tutorial 7-Segment-Anzeige]&lt;br /&gt;
&lt;br /&gt;
===Diskussionsbeiträge im Forum===&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/85248 Binär-zu-BCD in VHDL]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/83884 ebenfalls VHDL]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/59851 32 Bit in C]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/64842 16 und 32 Bit C und AVR-Assembler]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/1501 Assembler für AVR-GCC bis 256 Bit]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/173633 2 hoch x , wenn x kein Integer ist]&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/230317 schnelle dezimale Division auf AVR &amp;amp; Co.]&lt;br /&gt;
&lt;br /&gt;
== Sinus und Cosinus ==&lt;br /&gt;
&amp;amp;rarr; siehe [[AVR Arithmetik/Sinus und Cosinus (CORDIC)]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;amp;rarr; siehe [[AVR Arithmetik/Sinus und Cosinus (Lineare Interpolation)]]&lt;br /&gt;
&lt;br /&gt;
== Saturierung ==&lt;br /&gt;
&amp;amp;rarr; siehe [[AVR Arithmetik/Saturierung]]&lt;br /&gt;
&lt;br /&gt;
== Fußnoten ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Weblinks==&lt;br /&gt;
* [http://faculty.capitol-college.edu/~andresho/tutor/Multimedia/AVR/HW_mult/avr201.htm AVR201: Using the AVR Hardware Multiplier] (Implementierung und Beispiele)&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc1631.pdf AVR201: Using the AVR Hardware Multiplier] (Application Note, pdf, en)&lt;br /&gt;
* [http://www.azillionmonkeys.com/qed/sqroot.html Wurzelfunktion]&lt;br /&gt;
* [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=printview&amp;amp;t=37150&amp;amp;start=40 Optimierte Division durch 10]&lt;br /&gt;
* [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=printview&amp;amp;t=54192&amp;amp;start=20 Optimierte Division durch 3]&lt;br /&gt;
* [http://www.cs.uiowa.edu/~jones/bcd/divide.html Division durch reziproke Multiplikation, Tutorial]&lt;br /&gt;
* [http://web.archive.org/web/20080519123741/http://users.i.com.ua/~birua/math32.html Math32: Some Useful Assembler Multibyte Maths  Subroutines &amp;amp; Macrocalls] [http://avr-asm.tripod.com/math32x.html (oder hier)]&lt;br /&gt;
*[http://avr.15.forumer.com/a/computer-math-11/ AVR-Computer Math topics]&lt;br /&gt;
*[http://elm-chan.org/cc_e.html Elm-Chan&#039;s Bibliotheken u.a. Arithmetik]&lt;br /&gt;
* [http://www.embedded.com/design/opensource/217900224?printable=true Multiplication by a Fixed Point Constant], Embedded.com, 06/16/09&lt;br /&gt;
* [http://surfnet.dl.sourceforge.net/sourceforge/avrfix/avrfix.pdf M. Rosenblattl, A. Wolf: &#039;&#039;Fixed Point Library According to ISO/IEC Standard DTR 18037 for Atmel AVR Processors&#039;&#039;] (pdf, engl., 133 S.)&lt;br /&gt;
* [http://www.open-std.org/JTC1/sc22/wg14/www/docs/n1005.pdf ISO/IEC DTR 18037] (pdf, engl., 101 S.) &amp;amp;mdash; Spezifikation einer Erweiterung von C99 zur Unterstützung von Embedded Systems &lt;br /&gt;
* [http://www.phy6.org/outreach/edu/roman.htm A Different Kind of Multiplication]&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|AVR Arithmetik]]&lt;br /&gt;
[[Category:AVR-Arithmetik| ]]&lt;/div&gt;</summary>
		<author><name>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial_Bestellliste&amp;diff=62985</id>
		<title>AVR-Tutorial Bestellliste</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial_Bestellliste&amp;diff=62985"/>
		<updated>2012-01-02T20:27:06Z</updated>

		<summary type="html">&lt;p&gt;Mkleemann: Kategoriensortierung&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Bestellliste für das [[AVR-Tutorial]] bei Reichelt&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;&#039;&#039;&#039;Wichtiger Hinweis:&#039;&#039;&#039;:Die folgende Liste ist ziemlich veraltet. Die einzelnen Tutorialteile führen jeweils die benötigten Bauteile auf. Es empfiehlt sich, nach diesen Aufzählungen im Tutorial zu arbeiten, nicht nach der folgenden Liste! Und bitte bis zum Ende der Seite lesen.&lt;br /&gt;
&lt;br /&gt;
Die Liste ist allerdings nicht vollständig, da ich noch einige Teile hatte. Sie bezieht sich auf das AVR-Tutorial und den ATmega8 und [[ISP]]-Programmer. Es fehlen ein paar Widerstände und Kondensatoren sowie das LC-Display. Aus meiner Erfahrung heraus ist [http://www.reichelt.de Reichelt] der günstigste Versender mit fairen Versandkosten (5.45 Euro) und nur 10 Euro Mindestbestellsumme (dies soll keine Werbung etc. für Reichelt sein).&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;Bestellnummer               Beschreibung                                 Preis&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 µA 7805          Spannungsregler 1A positiv, TO-220   1                 0.23Euro&lt;br /&gt;
 MAX 232CPE       RS232-Treiber                                          0.75Euro&lt;br /&gt;
 GS 28            IC-Sockel, 28-polig, doppelter Federkontakt 2          0.16Euro&lt;br /&gt;
 H25PR150         Lochrasterplatine, Hartpapier, 150x100mm 2             3.20Euro&lt;br /&gt;
 SPL 20           IC-Fassung, 20-polig, einreihig, RM 2,54, gerade 3     0.78Euro&lt;br /&gt;
 ATMEGA 8-16 DIP  ATMega AVR-RISC-Controller, DIL-28 1                   4.25Euro&lt;br /&gt;
 STIFTL. 2X10W    Stiftleiste, 2x10-polig, vergoldet, zweireihig, gew. 2 0.26Euro&lt;br /&gt;
 74HCT 244        Bustreiber                                             0.25Euro&lt;br /&gt;
 RAD 1/63         Elektrolytkondensator, 5x11mm, RM 2,0mm 5              0.20Euro&lt;br /&gt;
 RAD 22/35        Elektrolytkondensator, 5x11mm, RM 2,0mm 5              0.20Euro&lt;br /&gt;
 D-SUB ST 25US    D-SUB-Stecker, 25-polig, gewinkelt, RM 7,2 1           0.40Euro&lt;br /&gt;
 OSZI 8,000000    Quarzoszillator, 8,00 MHz 1                            1.90Euro&lt;br /&gt;
 D-SUB BU 09US    D-SUB-Buchse, 9-polig, gewinkelt, RM 7,2 1             0.21Euro&lt;br /&gt;
 GS 40            IC-Sockel, 40-polig, doppelter Federkontakt 2          0.22Euro&lt;br /&gt;
 BAT 43           DIODE 1                                                0.11Euro&lt;br /&gt;
 HEBO 13          Hohlstecker-Printeinbaubuchse, gewinkelt 1             0.21Euro&lt;br /&gt;
 BUCHSENL 2X10G   Buchsenleiste, 2x10-polig, zweireihig, H: 8,3mm 2      0.30Euro&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;sortable&amp;quot; id=&amp;quot;bestellliste&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Bestellnummer&lt;br /&gt;
!Beschreibung&lt;br /&gt;
!Anzahl&lt;br /&gt;
!Gesamtpreis&lt;br /&gt;
|-&lt;br /&gt;
|µA 7805&lt;br /&gt;
|Spannungsregler 1A positiv, TO-220&lt;br /&gt;
|1&lt;br /&gt;
|0.17&lt;br /&gt;
|-&lt;br /&gt;
|MAX 232CPE&lt;br /&gt;
|RS232-Treiber&lt;br /&gt;
|1&lt;br /&gt;
|0.40&lt;br /&gt;
|-&lt;br /&gt;
|GS-28-S&lt;br /&gt;
|IC-Sockel, 28-polig, doppelter Federkontakt&lt;br /&gt;
|2&lt;br /&gt;
|0.16&lt;br /&gt;
|-&lt;br /&gt;
|H25PR150 &lt;br /&gt;
|Lochrasterplatine, Hartpapier, 150x100mm&lt;br /&gt;
|1&lt;br /&gt;
|1.60&lt;br /&gt;
|-&lt;br /&gt;
|SPL 20&lt;br /&gt;
|IC-Fassung, 20-polig, einreihig, RM 2,54, gerade&lt;br /&gt;
|3&lt;br /&gt;
|0.78&lt;br /&gt;
|-&lt;br /&gt;
|ATMEGA 8-16 DIP&lt;br /&gt;
|ATMega AVR-RISC-Controller, DIL-28&lt;br /&gt;
|1&lt;br /&gt;
|4.25&lt;br /&gt;
|-&lt;br /&gt;
|STIFTL. 2X10W&lt;br /&gt;
|Stiftleiste, 2x10-polig, vergoldet, zweireihig, gew.&lt;br /&gt;
|2&lt;br /&gt;
|0.26&lt;br /&gt;
|-&lt;br /&gt;
|74HCT 244&lt;br /&gt;
|Bustreiber&lt;br /&gt;
|1&lt;br /&gt;
|0.25&lt;br /&gt;
|-&lt;br /&gt;
|RAD 1/63&lt;br /&gt;
|Elektrolytkondensator, 5x11mm, RM 2,0mm&lt;br /&gt;
|5&lt;br /&gt;
|0.20&lt;br /&gt;
|-&lt;br /&gt;
|RAD 22/35&lt;br /&gt;
|Elektrolytkondensator, 5x11mm, RM 2,0mm&lt;br /&gt;
|5&lt;br /&gt;
|0.20&lt;br /&gt;
|-&lt;br /&gt;
|D-SUB ST 25US&lt;br /&gt;
|D-SUB-Stecker, 25-polig, gewinkelt, RM 7,2&lt;br /&gt;
|1&lt;br /&gt;
|0.40&lt;br /&gt;
|-&lt;br /&gt;
|OSZI 8,000000&lt;br /&gt;
|Quarzoszillator, 8,00 MHz&lt;br /&gt;
|1&lt;br /&gt;
|1.90&lt;br /&gt;
|-&lt;br /&gt;
|D-SUB BU 09US&lt;br /&gt;
|D-SUB-Buchse, 9-polig, gewinkelt, RM 7,2&lt;br /&gt;
|1&lt;br /&gt;
|0.21&lt;br /&gt;
|-&lt;br /&gt;
|GS 40&lt;br /&gt;
|IC-Sockel, 40-polig, doppelter Federkontakt&lt;br /&gt;
|2&lt;br /&gt;
|0.22&lt;br /&gt;
|-&lt;br /&gt;
|BAT 43&lt;br /&gt;
|Diode&lt;br /&gt;
|1&lt;br /&gt;
|0.11&lt;br /&gt;
|-&lt;br /&gt;
|HEBO 13&lt;br /&gt;
|Hohlstecker-Printeinbaubuchse, gewinkelt&lt;br /&gt;
|1&lt;br /&gt;
|0.21&lt;br /&gt;
|-&lt;br /&gt;
|BUCHSENL 2X10G&lt;br /&gt;
|Buchsenleiste, 2x10-polig, zweireihig, H: 8,3mm&lt;br /&gt;
|2&lt;br /&gt;
|0.30&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Versandkosten:  5,45 Euro&lt;br /&gt;
&lt;br /&gt;
Gesamtpreis:           19,83 Euro&lt;br /&gt;
&lt;br /&gt;
Alles ohne Gewähr für die Richtigkeit. Teilweise weichen die bestellten Artikel ab, da Reichelt nicht alles führt. Die Steckbretter bekommt man neuerdings günstig bei ebay (Suche: Steckbrett).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
Anm. mthomas 11.5.2004&lt;br /&gt;
&lt;br /&gt;
 was noch fehlen muesste:&lt;br /&gt;
 100 nF Kondensator (Folie) 7805 sekundaer&lt;br /&gt;
 100 nF Kondensator zw. VCC-GND am ATmega8&lt;br /&gt;
 100 nF Kondensator zw. AVCC-GND am ATmega8&lt;br /&gt;
 100 nF Kondensator zw. AREF-GND am ATmega8&lt;br /&gt;
 100 nF Kondensator zw. VCC-GND am MAX232&lt;br /&gt;
 100 nF Kondensator im Programmieradapter&lt;br /&gt;
 (ja...es sind ein paar, aber schaden tut&#039;s nichts)&lt;br /&gt;
&lt;br /&gt;
 für &amp;quot;Standardbeschaltung&amp;quot; Reset-Pin:&lt;br /&gt;
 100 nF Kondensator zw. Reset und GND&lt;br /&gt;
 10 kOhm Widerstand zw. Reset und VCC&lt;br /&gt;
 1N4148 o.ae. Diode zw. Reset und VCC&lt;br /&gt;
&lt;br /&gt;
 LEDs z.b. 3mm low-current (3mA)&lt;br /&gt;
 1,5k Ohm Vorwiderstaende für LEDs&lt;br /&gt;
&lt;br /&gt;
Änderungen:&lt;br /&gt;
&lt;br /&gt;
* für den MAX232 reichen 1µF Kondensatoren an den Ladepumpen&lt;br /&gt;
* Quarz besser mit einer &amp;quot;UART-Frequenz&amp;quot; also 3,6... oder 7,2... MHz&lt;br /&gt;
* DSUB-Buchse besser ohne Gehaeuse, verloetet sich einfacher auf Lochraster&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Bestellliste]]&lt;/div&gt;</summary>
		<author><name>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_7-Segment-Anzeige&amp;diff=62984</id>
		<title>AVR-Tutorial: 7-Segment-Anzeige</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_7-Segment-Anzeige&amp;diff=62984"/>
		<updated>2012-01-02T20:26:13Z</updated>

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

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

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

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

		<summary type="html">&lt;p&gt;Mkleemann: /* Literatur */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= AVR-Tutorial - Benötigte Ausrüstung =&lt;br /&gt;
==Hardware==&lt;br /&gt;
&lt;br /&gt;
Ein Mikrocontroller alleine ist noch zu nichts nützlich. Damit man etwas damit anfangen kann, braucht man eine Schaltung, in die der Controller eingesetzt wird. Dazu werden bei Elektronikhändlern Platinen angeboten, die alles nötige (Taster, LEDs, Steckverbinder...) enthalten. Häufig enthalten diese Platinen nicht nur Platz für den Mikroprozessor, sondern auch einen ISP-Programmierer (Näheres dazu später)&lt;br /&gt;
&lt;br /&gt;
=== Fertige Evaluations-Boards und Starterkits ===&lt;br /&gt;
&lt;br /&gt;
==== AVR Starterkit aus dem Mikrocontroller.net-Shop ====&lt;br /&gt;
&lt;br /&gt;
Sehr gut für dieses Tutorial geeignet ist das [http://shop.embedded-projects.net/index.php?module=artikel&amp;amp;action=artikel&amp;amp;id=108 AVR-Starterkit aus dem Mikrocontroller.net-Shop]. Das Kit enthält eine Platine mit dem Controller ATmega8, einen USB-ISP-Programmieradapter und ein Steckernetzteil. Die im Starterkit enthaltene [http://www.eproo.de/index.php?module=artikel&amp;amp;action=artikel&amp;amp;id=74 AVR Entwicklungsplatine für 28-pol. AVRs] gibt es auch einzeln. Diese enthält eine Fassung für den Controller, einen Spannungswandler, die Beschaltung für die serielle Schnittstelle und einen Anschluss für den Programmieradapter. Die restliche Hardware wie LEDs und Taster kann man sich selber nach Belieben auf das Lochrasterfeld löten.&lt;br /&gt;
&lt;br /&gt;
==== STK500 ====&lt;br /&gt;
[[Bild:Stk500.jpg|right]]&lt;br /&gt;
Das STK500 ist das Standard-Board für AVR Entwicklung, direkt von Atmel. Es enthält auch einen ISP-Programmer und ist fertig aufgebaut. Es ist unter Entwicklern sehr beliebt und wird natürlich von Atmel unterstützt. Es gilt allgemein als gute Investition, wenn man ernsthaft in das Thema einsteigen möchte.&lt;br /&gt;
&lt;br /&gt;
Das STK500 kostet bei Reichelt ca. 80 Euro (ein geeignetes Netzteil muss zusätzlich erworben werden).&lt;br /&gt;
&lt;br /&gt;
==== Pollin ATMEL Evaluations-Board Version 2.x ====&lt;br /&gt;
&lt;br /&gt;
Bei Pollin Elektronik gibt es für ca. 15 Euro ein Evaluations-Board als Bausatz zum Selbstlöten. Im Bausatz sind die Aufbauanleitung, die Platine und Bauteile enthalten. Der/die Mikrocontroller und eine Stromversorgung müssen separat beschafft werden. Auf dem Board ist ein einfacher ISP-Programmer (serielles &#039;&#039;bit-banging&#039;&#039;) integriert. &lt;br /&gt;
&lt;br /&gt;
Siehe: &lt;br /&gt;
* [[Pollin ATMEL Evaluations-Board]]&lt;br /&gt;
* http://www.pollin.de&lt;br /&gt;
&lt;br /&gt;
==== Pollin Funk-AVR-Evaluationsboard v1.x ====&lt;br /&gt;
&lt;br /&gt;
Bei diesem Board besteht die Möglichkeit, Funkmodule wie das [[RFM12]], RFM01 oder RFM02 auf dem Board aufzulöten.&lt;br /&gt;
&lt;br /&gt;
Siehe: &lt;br /&gt;
* [[Pollin Funk-AVR-Evaluationsboard]]&lt;br /&gt;
* [http://www.pollin.de http://www.pollin.de]&lt;br /&gt;
&lt;br /&gt;
==== Rumpus Board von lochraster.org ====&lt;br /&gt;
&lt;br /&gt;
Lochraster.org bietet ein Entwicklungsboard namens Rumpus an. Es kommt als Bausatz mit allen Teilen und Microcontroller (Atmega 168), auf dem Microcontroller ist bereits ein USB Bootloader installiert so dass man nach dem Zusammenbau sofort starten kann. Das Board wird direkt über USB mit Strom versorgt und auch über USB programmiert, es kann auch selbst als Programmer für AVR Microcontroller benutzt  werden. Das Board ist mit recht umfangreicher Peripherie ausgestattet, so das sich von sehr einfachen Anwendungen wie dem Blinken einer LED bis hin zu komplexen Aufgaben wie senden und empfangen von Infrarot Signalen eine Vielzahl von Anwendungen realisieren lassen. Mit 45 Euro gehört es sicher nicht zu den ganz billigen Einsteigerboards, für den ambitionierten Amateur bietet die reichhaltige Peripherie den Vorteil, das Board während des gesamten Lernprozesses zu nutzen ohne für die Realisierung komplexerer Aufgaben neue Hardware auflöten zu müssen. Auch relativiert sich dieser Preis wieder dadurch, dass kein ISP Programmer benötigt wird. Beim Umstieg auf ein anderes Board, für welches man dann einen ISP Programmer benötigt, kann der Rumpus diese Aufgabe übernehmen anstatt zum alten Eisen geworfen zu werden (s. Infos im [http://www.mikrocontroller.net/topic/217122#2165435 Forumbeitrag von Sebastian Noack]).&lt;br /&gt;
&lt;br /&gt;
Weitere Infos unter http://www.lochraster.org/ und http://wiki.lochraster.org/&lt;br /&gt;
&lt;br /&gt;
==== RN-Control ====&lt;br /&gt;
&lt;br /&gt;
Die Forengemeinde von Roboternetz hat ebenfalls ein Evaluierungsboard entwickelt, das mittlerweile sehr ausgereift ist und viele Erweiterungsmöglichkeiten bietet.&lt;br /&gt;
&lt;br /&gt;
Siehe:&lt;br /&gt;
* [http://robotikhardware.de/ http://robotikhardware.de/]&lt;br /&gt;
* [http://www.roboternetz.de/ http://www.roboternetz.de/]&lt;br /&gt;
&lt;br /&gt;
==== Arduino ====&lt;br /&gt;
Die Boards der [http://www.arduino.cc Arduino-Familie] bieten z.B. einen ATmega328P mit 16MHz und lassen sich über einen integrierten USB-seriell-Wandler und [[Bootloader]] programmieren. Die Ports sind auf Buchsenleisten herausgeführt. Arduino-Boards können auch unabhängig von der Arduino-Entwicklungsumgebung (Arduino-IDE) als AVR-Entwicklungsboard genutzt werden.&lt;br /&gt;
&lt;br /&gt;
==== Andere ====&lt;br /&gt;
&lt;br /&gt;
Das Angebot an AVR-Evaluationboards, -Experimentierplatinen, -Entwicklerplatinen oder wie die jeweiligen Hersteller ihre Produkte auch immer bezeichnen, ist mittlerweile recht groß geworden. Sie alle zu bewerten ist unmöglich geworden.&lt;br /&gt;
&lt;br /&gt;
===Selbstbau===&lt;br /&gt;
&lt;br /&gt;
Ein fertiges Board ist gar nicht nötig, man kann die benötigte Schaltung auch selbst auf einem kleinen Steckbrett oder einer Lochrasterplatine aufbauen. So kompliziert wie das STK500 wird es nicht, es reichen eine Handvoll Bauteile. Wie man das macht, wird im Folgenden beschrieben.&lt;br /&gt;
Steckbretter (Breadboards) gibt&#039;s z.&amp;amp;nbsp;B. bei [http://www.reichelt.de Reichelt],  [http://www.conelek.com/Steckplatinen ConeleK], [http://www.elv.de/ ELV] oder [http://www.conrad.de/ Conrad]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Mega8_Tutorial.png|center|framed| Die Grundschaltung eines Mega8.&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039; Die Pinbelegung der 6-poligen ISP-Verbindung weicht von den ATMEL Angaben ab! Wenn ATMEL oder ATMEL-kompatible ISP-Adapter benutzt werden, die Pinbelegung aus [http://www.atmel.com/dyn/resources/prod_documents/doc2521.pdf AVR042] (PDF) bzw. [[AVR_In_System_Programmer#Pinbelegung]] benutzen]]&lt;br /&gt;
&lt;br /&gt;
Über den Takteingang &#039;&#039;&#039;XTAL1&#039;&#039;&#039; ist der Mikrocontroller mit dem &#039;&#039;&#039;Quarzoszillator&#039;&#039;&#039; verbunden, der den benötigten Takt von 4 MHz liefert (siehe unten). Achtung: die Pins werden, wenn man den Oszillator mit der Schrift nach oben vor sich liegen hat, von unten links aus abgezählt. Unten links ist Pin 1, unten rechts Pin 7, oben rechts Pin 8 und oben links Pin 14 (natürlich hat der Oszillator nur 4 Pins. Die Nummerierung kommt daher, dass bei einem normalen IC dieser Größe an den gleichen Positionen die Pins Nr. 1, 7, 8 und 14 wären). Zu den Pins Datenblatt beachten [http://www.mikrocontroller.net/topic/204429#2015503].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PD0-PD7&#039;&#039;&#039; und &#039;&#039;&#039;PB0-PB5&#039;&#039;&#039; sind die &#039;&#039;&#039;IO-Ports&#039;&#039;&#039; des Mikrocontrollers. Hier können Bauteile wie LEDs, Taster oder LCDs angeschlossen werden.&lt;br /&gt;
Der &#039;&#039;&#039;Port C (PC0-PC5)&#039;&#039;&#039; spielt beim Atmega8/AT90S4433 eine Sonderrolle: mit diesem Port können Analog-Spannungen gemessen werden. Aber dazu später mehr!&lt;br /&gt;
An &#039;&#039;&#039;Pin 17-19&#039;&#039;&#039; ist die Stiftleiste zur Verbindung mit dem ISP-Programmer angeschlossen, über den der AVR vom PC programmiert wird (Achtung: Pins in Abbildung entsprechen nicht der Belegung des AVRISP mkII. Die korrekte Pin-Belegung kann im Handbuch des AVRISP mkII eingesehen werden).&lt;br /&gt;
Die Resetschaltung, bestehend aus &#039;&#039;&#039;R1&#039;&#039;&#039; und &#039;&#039;&#039;C1&#039;&#039;&#039;, sorgt dafür, dass der Reseteingang des Controllers standardmäßig auf Vcc=5V liegt.&lt;br /&gt;
Zum Programmieren zieht der ISP-Adapter die Resetleitung auf Masse (GND), die Programmausführung wird dadurch unterbrochen und der interne Speicher des Controllers kann neu programmiert werden.&lt;br /&gt;
Zwischen Vcc und GND kommen noch jeweils ein 100nF Keramik- oder Folienkondensator C3 und C4, um Störungen in der Versorgungsspannung zu unterdrücken. Diese [[Abblockkondensator|Abblockkondensatoren]] sollten so nah wie möglich am Controller plaziert werden. An den Ausgang ARef wird ebenfalls ein 100nF Kondensator angeschlossen. Dieser wird allerdings erst benötigt, wenn der Analog/Digital Konverter des µC in Betrieb genommen wird.&lt;br /&gt;
&lt;br /&gt;
Für den Anschluss des ISP-Programmiergerätes kann man im Grunde jede beliebige Pin-Belegung des ISP Steckers benutzen, solange nur alle benötigten Leitungen mit dem Programmiergerät verbunden sind. In der Praxis haben sich allerdings bestimmte Belegungen durchgesetzt. Im Schaltbild ist eine &#039;&#039;&#039;eigene&#039;&#039;&#039; Belegung des 6-poligen Steckers gezeigt. Die alternative Pinbelegung eines 2-reihigen/10-poligen Steckers ist eine übliche Belegung. Benutzt man so eine übliche Belegung, so reicht normalerweise ein 10-poliges Flachbandkabel, um den vorhandenen ISP-Programmer so mit der Schaltung zu verbinden, dass alle Signale am richtigen Prozessorpin ankommen. Siehe auch [[AVR_In_System_Programmer]].&lt;br /&gt;
&lt;br /&gt;
Hier die Liste der benötigten Bauteile: &lt;br /&gt;
&lt;br /&gt;
* R1         Widerstand 10 kOhm&lt;br /&gt;
* C1         Keramikkondensator 47 nF&lt;br /&gt;
* C2, C3, C4 Keramik- oder Folienkondensator 100 nF&lt;br /&gt;
*            Stiftleiste 6-polig&lt;br /&gt;
*            Mikrocontroller ATmega8 (kann auf [http://shop.mikrocontroller.net/ http://shop.mikrocontroller.net/] bestellt werden)&lt;br /&gt;
*            Quarzoszillator 4 MHz&lt;br /&gt;
&lt;br /&gt;
Beim Steckbrett ist darauf zu achten, dass man die parallellaufenden Schienen für GND (blau) und Vcc (rot) jeweils mit Drähten verbindet (nicht Vcc und GND miteinander!).&lt;br /&gt;
&lt;br /&gt;
Eine Zusammenstellung der benötigten Bauteile befindet sich in der [[AVR-Tutorial_Bestellliste|Bestellliste]].&lt;br /&gt;
&lt;br /&gt;
Eine weitere Beschreibung für ein Minimalsystem gibt es [http://conelek.org/Mikrocontroller_Minimalsystem_mit_AVR_ATMega8 hier.]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid grey; padding: 1ex; font-size: 90%;&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Ergänzende Hinweise zur Taktversorgung (kann übersprungen werden) ===&lt;br /&gt;
&lt;br /&gt;
Ein Mikrocontroller benötigt, wie jeder Computer, eine Taktversorgung. Der Takt ist notwendig, um die internen Abläufe im Prozessor in einer zeitlich geordneten Reihenfolge ausführen zu können. Die Frequenz des Taktes bestimmt im Wesentlichen, wie schnell ein Mikrocontroller arbeitet. Bei einem ATMega8 gibt es viele Möglichkeiten zur Taktversorgung, die wichtigsten sollen hier gezeigt werden.&lt;br /&gt;
&lt;br /&gt;
* interner RC-Oszillator, das ist der Auslieferungszustand&lt;br /&gt;
* Keramikresonator&lt;br /&gt;
* Quarz&lt;br /&gt;
* Quarzoszillator&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Vergleich der AVR-Taktquellen&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Typ || Genauigkeit || Vorteile || Nachteile&lt;br /&gt;
|-&lt;br /&gt;
| interner&amp;lt;br&amp;gt;RC-Oszillator || 1-5% || Xtal1/2 Pins verfügbar&amp;lt;br&amp;gt;kostenlos&amp;lt;br&amp;gt;kein Platzbedarf&amp;lt;br&amp;gt;schnellstes Einschwingen (wenige Takte) || ungenau&lt;br /&gt;
|-&lt;br /&gt;
| Keramikresonator || 0,5-1% || ausreichend genau für [[UART]]&amp;lt;br&amp;gt;in sehr hohen Stückzahlen billiger als Quarz&amp;lt;br&amp;gt;schnelleres Einschwingen als Quarz (ca. 1ms) || XTAL1/2 Pins nicht verfügbar&amp;lt;br&amp;gt;Platzbedarf&lt;br /&gt;
|-&lt;br /&gt;
| Quarz || 10-100ppm || sehr genau&amp;lt;br&amp;gt;temperaturstabil || XTAL1/2 Pins nicht verfügbar&amp;lt;br&amp;gt;Platzbedarf&amp;lt;br&amp;gt;Kosten bei sehr hohen Stückzahlen (1000++)&amp;lt;br&amp;gt;langsames Anschwingen (ca. 10ms)&lt;br /&gt;
|-&lt;br /&gt;
| Quarzoszillator || 1-100ppm || hochgenau&amp;lt;br&amp;gt;sehr temperaturstabil&amp;lt;br&amp;gt;liefert selbst ein Signal, kann dadurch [[AVR_Fuses#Taktquellen_Fuse_Einstellung | verfuste AVRs]] retten&amp;lt;br&amp;gt;kann mehrere Takteingänge treiben || XTAL1 Pin nicht verfügbar&amp;lt;br&amp;gt;Platzbedarf&amp;lt;br&amp;gt;Kosten bei sehr hohen Stückzahlen (1000++)&amp;lt;br&amp;gt;langsames Anschwingen (ca. 10ms)&lt;br /&gt;
|}&lt;br /&gt;
1ppm = 0,0001% (engl. one &#039;&#039;&#039;p&#039;&#039;&#039;art &#039;&#039;&#039;p&#039;&#039;&#039;er &#039;&#039;&#039;m&#039;&#039;&#039;illion, der millionste Teil) &lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;FF0000&amp;quot;&amp;gt;Achtung: Ein ATMega8 wird mit aktiviertem internen RC-Oszillator ausgeliefert. Um eine andere Taktquelle zu aktivieren, müssen die [[AVR Fuses#Taktquellen Fuse Einstellung|AVR Fuses]] des Prozessors verändert werden. Das muss jedoch sehr vorsichtig gemacht werden, siehe Artikel.&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Keramikresonator ====&lt;br /&gt;
&lt;br /&gt;
Die Anbindung des Keramikresonators sieht so aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Resonator.png|framed|center| Resonator Standardbeschaltung]]&lt;br /&gt;
&lt;br /&gt;
Es werden keine Kondensatoren benötigt, diese sind schon eingebaut, daher ist der Anschluss eines Keramikschwingers kinderleicht. Achtung: Keramikresonatoren gibt es mit zwei oder drei Pins. Nur die mit drei Pins besitzen interne Kondensatoren.&lt;br /&gt;
&lt;br /&gt;
==== Quarz ====&lt;br /&gt;
&lt;br /&gt;
Die Anbindung des Quarzes sieht so aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:tutorial-quarz-schaltplan.png|center|framed| Quarz Standardbeschaltung]]&lt;br /&gt;
&lt;br /&gt;
Die beiden Kondensatoren &#039;&#039;&#039;C3&#039;&#039;&#039; und &#039;&#039;&#039;C4&#039;&#039;&#039; sind zum Betrieb des Quarzes notwendig. Ihre Größe ist abhängig von den Daten des Quarzes. 22pF sind ein Wert, der bei den meisten Quarzen funktionieren sollte.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Spannungsversorgung ===&lt;br /&gt;
&lt;br /&gt;
Die Versorgungsspannung &#039;&#039;&#039;Vcc&#039;&#039;&#039; beträgt 5V und kann z.&amp;amp;nbsp;B. mit folgender Schaltung erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
[[Bild:V_Regler.gif|framed|center|Standard-Netzteilbeschaltung eines 7805]]&lt;br /&gt;
&lt;br /&gt;
Bauteile:&lt;br /&gt;
* IC1: 5V-Spannungsregler 7805&lt;br /&gt;
* C1: Elko 10µF (Polung beachten!)&lt;br /&gt;
* C2,C3: 2x Kondensator 100nF (kein Elektrolyt)&lt;br /&gt;
* D1: Diode 1N4001&lt;br /&gt;
&lt;br /&gt;
Hauptelement der Schaltung ist das IC 7805. Seine Aufgabe ist es aus der Versorgungsspannung stabile 5V zu erzeugen. Dieses IC gibt es seit vielen Jahren und wird von vielen Chipherstellern produziert. Er stellt die einfachste und simpelste Möglichkeit dar, aus einer vorhandenen Gleichspannung definierte 5V zu erzeugen. Den 7805 gibt es in verschiedenen Ausführungen, was seine maximale Strombelastung angeht. Für die Zwecke dieses Tutorials ist die Standard-Variante, welche maximal 1A abgeben kann, völlig ausreichend. Der 7805 enthält eine Übertemperatursicherung, so dass er abschaltet, wenn es ihm zu heiß wird.&lt;br /&gt;
&lt;br /&gt;
Die beiden 100nF Kondensatoren haben die Aufgabe, eine mögliche Schwingneigung des 7805 zu unterdrücken. Sie müssen so nahe wie möglich an den Anschlusspins des 7805 angeschlossen werden, um ihre Wirkung zu entfalten.&lt;br /&gt;
&lt;br /&gt;
An den Eingang (+ und - im Schaltplan) wird ein Steckernetzteil mit einer Spannung von 7 - 12V angeschlossen. Der 7805 benötigt an seinem Eingang eine Gleichspannung, die mindestens 7V beträgt. Auf der anderen Seite macht es auch keinen Sinn, wesentlich über 12V Eingangsspannung hinauszugehen. Der 7805 ist ein Linearregler. Salopp gesagt, wird die überschüssige Spannung in Form von Wärme vernichtet. Liegt die Eingangsspannung weit über 12V, so wird schon wesentlich mehr Energie in Form von Wärme umgesetzt, als am Ausgang entnommen werden kann. Mal ganz davon abgesehen, dass der 7805 davon brennend heiß werden wird.&lt;br /&gt;
&lt;br /&gt;
Hier ein paar kleine Rechenbeispiele:&lt;br /&gt;
12V Eingangsspannung - 5V Ausgangsspannung = 7V Differenz x 0,1A Strombedarf der Schaltung ergibt die Verlustwärme die abgeführt werden muss.&lt;br /&gt;
&lt;br /&gt;
7V x 0,1A = 0,7 Watt&lt;br /&gt;
&lt;br /&gt;
Wenn man jetzt eine Eingangsspannung von 7V nimmt, so dass die Mindestdifferenz von 2V noch eingehalten wird kommen wir zu diesen Werten&lt;br /&gt;
&lt;br /&gt;
2V x  0,1A = 0,2 Watt Abwärme&lt;br /&gt;
2V x 0,35A = 0,7 Watt Abwärme oder anders gesagt wir können der Schaltung 350mA entnehmen und haben die gleiche Abwärme wie im oberen Beispiel mit nur 100mA Stromentnahme.&lt;br /&gt;
&lt;br /&gt;
Man sieht das man die Eingangsspannung so klein wie möglich wählen sollte um dadurch die Verluste in Grenzen halten zu können. Außerdem ist es meist so das für eine geringere Stromentnahme auch ein eine niedrigere Differenzspannung ausreicht. In manchen Datenblätter ist z.B. angegeben 0,5A = 1V Dropvoltage und bei 1A = 2V Dropvoltage....&lt;br /&gt;
&lt;br /&gt;
Weiterhin sei gesagt das es so genannte Low Drop, Ultra Low Drop...Regler gibt, die mit einer viel kleineren Differenz zw. Ein- und Ausgangsspannung zurechtkommen, wodurch man die Verluste noch weiter drücken kann.&lt;br /&gt;
&lt;br /&gt;
Eine Stromversorgung mit Batterien ist grundsätzlich auch möglich, wenn die elektrischen Grenzdaten des µC eingehalten werden (max. Spannung, min. Spannung). Bei der geregelten Stromversorgung oben sollte die Batteriespannung ca. 1.5 - 2.5V (Dropout-Spannung des Linearreglers) größer sein als die Versorgungsspannung des µC. Die [[Versorgung aus einer Zelle]] ist ein Thema für Fortgeschrittene.&lt;br /&gt;
&lt;br /&gt;
=== Beispielhafter Aufbau auf einem [[Steckbrett]] ===&lt;br /&gt;
&lt;br /&gt;
Auf einem [[Steckbrett]] könnte eine Schaltung etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
[[Bild:tutorial_grundschaltung_breadboard.jpg|600px|center|Steckbrett mit Selbstschaltung Atmega8 und Quarz als externe Taktquelle]]&lt;br /&gt;
&lt;br /&gt;
Hier ist die oben beschriebene Selbstbauschaltung zu sehen.  Spannungsversorgung (links), 6-poliger ISP-Anschluss (rechts hinter dem µC), Quarz mit 2 Kondensatoren statt Oszillator, erweitert um eine LED mit Vorwiderstand an PB0 (rechts vor dem µC), einem Resettaster (links vor dem µC) und einem Stützkondensator zwischen +5V und GND (rechts unten).&lt;br /&gt;
&lt;br /&gt;
=== Der ISP-Programmierer (In-System-Programmer)===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Mikrocontroller.gif|framed|right|ISP Programmierer]]&lt;br /&gt;
Dann braucht man nur noch den &#039;&#039;&#039;ISP-Programmieradapter&#039;&#039;&#039;, über den man die Programme vom PC in den Controller übertragen kann. Eine Übersicht über mögliche ISP-Programmer Varianten findet sich im Artikel [[AVR_In_System_Programmer]].&lt;br /&gt;
&lt;br /&gt;
Fertige ISP-Programmer zum Anschluss an den Parallelport oder USB gibt es z.&amp;amp;nbsp;B. auf http://shop.mikrocontroller.net/. &lt;br /&gt;
&lt;br /&gt;
Eine Bauanleitung gibt es u.a. auf [http://www.rn-wissen.de/index.php/AVR-ISP_Programmierkabel http://www.rn-wissen.de/index.php/AVR-ISP_Programmierkabel] oder [http://rumil.de/hardware/avrisp.html http://rumil.de/hardware/avrisp.html].&lt;br /&gt;
&lt;br /&gt;
Den ISP-Adapter schließt man an den Parallelport an und verbindet ihn mit der Stiftleiste SV1 über ein 6-adriges Kabel (siehe Schaltplan).&lt;br /&gt;
&lt;br /&gt;
=== Sonstiges ===&lt;br /&gt;
&lt;br /&gt;
Wer vorausschauend kauft, kauft mehr als einen Mikrocontroller. Bis der erste Controller defekt ist, oder man durch Austauschen sicher gehen möchte, ob der Fehler im Programm oder im Controller ist, vergeht nur wenig Zeit.&lt;br /&gt;
&lt;br /&gt;
Für die anderen Teile des Tutorials sollte man sich noch die folgenden Bauteile besorgen: &lt;br /&gt;
&lt;br /&gt;
---------------------------&lt;br /&gt;
Teil 2 (I/O-Grundlagen)&lt;br /&gt;
* 6 LEDs 5mm (Standard-LED, ruhig auch in unterschiedlichen Farben, rot/gelb/grün)&lt;br /&gt;
* 5 Taster&lt;br /&gt;
* 6 Widerstände 1k&lt;br /&gt;
* 5 Widerstände 10k&lt;br /&gt;
&lt;br /&gt;
---------------------------&lt;br /&gt;
Teil 6 (LC-Display)&lt;br /&gt;
* 1 Potentiometer 10k&lt;br /&gt;
* 1 HD44780-kompatibles LCD, z.&amp;amp;nbsp;B. 4x20 oder 2x16 Zeichen&lt;br /&gt;
*   besitzt das LCD eine Hintergrundbeleuchtung, dann noch einen Vorwiderstand dafür. Details dazu stehen im Datenblatt des LCD. Ein Wert von 50Ω sollte aber in jedem Fall passen. Schlimmstenfalls ist die Hintergrundbeleuchtung dann etwas zu dunkel.&lt;br /&gt;
&lt;br /&gt;
---------------------------&lt;br /&gt;
Teil 10 (Der UART)&lt;br /&gt;
* 1 Pegelwandler MAX232, MAX232&#039;&#039;&#039;A&#039;&#039;&#039; oder MAX202&lt;br /&gt;
* 5 Kondensatoren&lt;br /&gt;
** Bei einem MAX232: je 1µF Elektrolytkondensator&lt;br /&gt;
** Bei einem MAX202 oder MAX232&#039;&#039;&#039;A&#039;&#039;&#039;: je  100nF Keramik- oder Elektrolytkondensator&lt;br /&gt;
:Die Kondensatoren dürfen auch größer sein. Ist man sich nicht sicher, welchen MAX232 man hat (A oder nicht A), dann die größeren Kondensatoren 1µF nehmen, die funktionieren auch beim MAX232A oder MAX202.&lt;br /&gt;
* 1 9-polige SUBD-Buchse (female)&lt;br /&gt;
* 1 dazu passendes Modem(nicht Nullmodem!)-Kabel&lt;br /&gt;
&lt;br /&gt;
---------------------------&lt;br /&gt;
Teil 14 (ADC)&lt;br /&gt;
* 1 Kondensator 100n&lt;br /&gt;
* 1 Potentiometer 10k&lt;br /&gt;
* nach Lust und Laune temperatur- oder lichtabhängige Widerstände und jeweils einen Widerstand in der gleichen Größenordnung wie der Sensor&lt;br /&gt;
&lt;br /&gt;
---------------------------&lt;br /&gt;
Teil 17 (Schieberegister)&lt;br /&gt;
* 2 Schieberegister 74HC595&lt;br /&gt;
* einige LED, damit man an die Schieberegister auch etwas anschliessen kann, samt passenden Vorwiderständen&lt;br /&gt;
&lt;br /&gt;
---------------------------&lt;br /&gt;
Teil 19 (7-Segmentanzeige)&lt;br /&gt;
* 4 7-Segmentanzeigen mit gemeinsamer Anode&lt;br /&gt;
* 4 PNP Transistoren BC328&lt;br /&gt;
* 4 Widerstände 1k&lt;br /&gt;
* 7 Widerstände 100Ω&lt;br /&gt;
&lt;br /&gt;
Für weitere Bauteile, die man als angehender µC Bastler auch des Öfteren mal benötigt, empfiehlt sich ein Blick in die Liste der [[Standardbauelemente]] bzw. in die [[Absolute_beginner|Grundausstattung]]. Wenn Ihr Händler Großpackungen (zb. 100 Stück) von 100n Kondensatoren, 10k, 1k oder 100Ω Widerständen anbietet, sollten Sie deren Erwerb in Erwägung ziehen. Diese Bauteile benötigt man oft, und derartige Großpackungen sind meist nicht teurer, als wennn man einige wenige Exemplare einzeln kauft. Dies hängt damit zusammen, dass das Herauszählen von 9 Bauteilen für den Verkäufer teurer kommt, als 100 Bauteile abgepackt aus dem Regal zu nehmen.&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorial wird nur auf die Programmierung in Assembler eingegangen, da Assembler für das Verständnis der Hardware am besten geeignet ist.&lt;br /&gt;
&lt;br /&gt;
=== Assembler ===&lt;br /&gt;
&lt;br /&gt;
Zuerst braucht man einen &#039;&#039;&#039;Assembler&#039;&#039;&#039;, der in Assemblersprache geschriebene Programme in Maschinencode übersetzt. Windows-User können das [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR-Studio] von Atmel verwenden, das neben dem Assembler auch einen Simulator enthält, mit dem sich die Programme vor der Übertragung in den Controller testen lassen; für Linux gibt es [http://www.tavrasm.org/ tavrasm], [http://avra.sourceforge.net/ avra] und [http://avr-asm-tutorial.net/gavrasm/index_de.html gavrasm]. &lt;br /&gt;
&lt;br /&gt;
Um die vom Assembler erzeugte &amp;quot;.hex&amp;quot;-Datei über den ISP-Adapter in den Mikrocontroller zu programmieren, kann man unter Windows z.&amp;amp;nbsp;B. das Programm [http://www.myplace.nu/avr/yaap/ yaap] verwenden, für Linux gibt es [http://savannah.nongnu.org/projects/uisp/ uisp], für beide avrdude.&lt;br /&gt;
&lt;br /&gt;
=== C ===&lt;br /&gt;
Wer in C programmieren möchte, kann den kostenlosen GNU-C-Compiler AVR-GCC (unter Windows &amp;quot;WinAVR&amp;quot;) ausprobieren. Dieser C-Compiler kann auch in das für Assembler-Programmierung notwendige AVR-Studio integriert werden. In der Artikelsammlung gibt es ein umfangreiches [[AVR-GCC-Tutorial|Tutorial]] zu diesem Compiler;&lt;br /&gt;
&lt;br /&gt;
Wer unter Windows und Linux gleichermassen kostenlos 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. Hier wird auch der AVR-GCC benutzt. In der Artikelsammlung gibt es ein umfangreiches [[AVR Eclipse|AVR Eclipse Tutorial]] zu dieser IDE.&lt;br /&gt;
Ebenfalls unter Linux und Windows verfügbar ist die Entwicklungsumgebung [http://www.codeblocks.org/ Code::Blocks] (aktuelle, stabile Versionen sind als Nightly Builds regelmäßig im [http://forums.codeblocks.org/ Forum] verfügbar). Innerhalb dieser Entwicklungsumgebung können ohne die Installation zusätzlicher Plugins &amp;quot;AVR-Projekte&amp;quot; angelegt werden.&lt;br /&gt;
&lt;br /&gt;
Fragen dazu stellt man am besten hier im [http://www.mikrocontroller.net/forum/list-2-1.html GCC-Forum].&lt;br /&gt;
&lt;br /&gt;
=== Pascal ===&lt;br /&gt;
Wer in Pascal programmieren muss, kann [http://www.e-lab.de AVRPascal] ausprobieren.&amp;lt;br&amp;gt; &lt;br /&gt;
Dieser Pascalcompiler ist kostenfrei bis 4kb Code und bietet viele ausgereifte Bibliotheken für Servoansteuerung, Serielle Schnittstellen (COM, TWI, SPI), PWM, Timernutzung, LC-Displays usw.&amp;lt;br&amp;gt; &lt;br /&gt;
Außerdem gibt es eine kostenfreie Version für den Mega8 und den Mega88.&lt;br /&gt;
[http://www.e-lab.de E-LAB].&lt;br /&gt;
&lt;br /&gt;
=== Basic ===&lt;br /&gt;
Auch Basic-Fans kommen nicht zu kurz, für die gibt es z.&amp;amp;nbsp;B. [[Bascom AVR]] ($69, Demo verfügbar).&lt;br /&gt;
&lt;br /&gt;
=== Forth ===&lt;br /&gt;
Wer einen direkten und interaktiven Zugang zum Controller haben will, sollte sich [http://amforth.sourceforge.net Forth] anschauen. Voraussetzung ist ein serieller Anschluß (Max232), also etwas mehr als die Minimalbeschaltung.&lt;br /&gt;
&lt;br /&gt;
== Literatur ==&lt;br /&gt;
Bevor man anfängt, sollte man sich die folgenden PDF-Dateien runterladen und zumindest mal reinschauen:&lt;br /&gt;
&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf Datenblatt des ATmega8 (4,54 MB)]&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf Befehlssatz der AVRs (1,27 MB)]&lt;br /&gt;
* oder [http://www.avr-roboter.de/controller/befehle/befehle.html Befehlssatz in deutscher Übersetzung online]&lt;br /&gt;
* oder [http://www.avr-modelleisenbahn.de/atmega8/0-einleitung.htm Datenblatt des ATmega8 in deutscher Übersetzung online]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Datenblatt eines Controllers ist das wichtigste Dokument für einen Entwickler. Es enthält Informationen über die Pinbelegung, Versorgungsspannung, Beschaltung, Speicher, die Verwendung der IO-Komponenten und vieles mehr.&lt;br /&gt;
&lt;br /&gt;
Im Befehlssatz sind alle Assemblerbefehle der AVR-Controllerfamilie aufgelistet und erklärt.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
 &lt;br /&gt;
{{Navigation_hochvor|	 &lt;br /&gt;
hochtext=Inhaltsverzeichnis|	 &lt;br /&gt;
hochlink=AVR-Tutorial|	 &lt;br /&gt;
vortext=I/O Grundlagen|	 &lt;br /&gt;
vorlink=AVR-Tutorial: IO-Grundlagen}}	 &lt;br /&gt;
 &lt;br /&gt;
[[Category:AVR-Tutorial|Equipment]]&lt;/div&gt;</summary>
		<author><name>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_IO-Grundlagen&amp;diff=62979</id>
		<title>AVR-Tutorial: IO-Grundlagen</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_IO-Grundlagen&amp;diff=62979"/>
		<updated>2012-01-02T20:23:49Z</updated>

		<summary type="html">&lt;p&gt;Mkleemann: /* Ausgänge benutzen, wenn mehr Strom benötigt wird */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Hardware ==&lt;br /&gt;
Für die ersten Versuche braucht man nur ein paar Taster und [[LED]]s an die IO-Ports des AVRs anzuschließen. An &#039;&#039;&#039;PB0-PB5&#039;&#039;&#039; schließt man 6 LEDs über einen Vorwiderstand von je 1 kΩ gegen Vcc (5V) an. In der Praxis ist es unerheblich, ob der Widerstand vor oder nach der Diode liegt, wichtig ist nur, dass er da ist. Weitere Details zu LEDs und entsprechenden Vorwiderständen findet ihr im Artikel über [[LED]]s und in diesem [http://www.mikrocontroller.net/topic/66109 Thread im Forum].&lt;br /&gt;
&lt;br /&gt;
[[Bild:Led.gif|framed|center| Standard Led Anschluss]]&lt;br /&gt;
&lt;br /&gt;
Dass die LEDs an den gleichen Pins wie der ISP-Programmer angeschlossen sind, stört übrigens normalerweise nicht. Falls wider Erwarten deshalb Probleme auftreten sollten, kann man versuchen, den Vorwiderstand der LEDs zu vergrößern.&lt;br /&gt;
&lt;br /&gt;
An &#039;&#039;&#039;PD0-PD3&#039;&#039;&#039; kommen 4 Taster mit je einem 10 kΩ Pullup-Widerstand:&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bild:Taster.gif|framed|center| Standard Taster Anschluss]]&lt;br /&gt;
&lt;br /&gt;
==Zahlensysteme==&lt;br /&gt;
Bevor es losgeht, hier noch ein paar Worte zu den verschiedenen Zahlensystemen.&lt;br /&gt;
&lt;br /&gt;
Binärzahlen werden für den Assembler im Format &#039;&#039;&#039;0b00111010&#039;&#039;&#039; geschrieben, Hexadezimalzahlen als &#039;&#039;&#039;0x7F&#039;&#039;&#039;. Umrechnen kann man die Zahlen z.&amp;amp;nbsp;B. mit dem Windows-Rechner. Hier ein paar Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! Dezimal || Hexadezimal || Binär &lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0 &lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x00&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00000000&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x01&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00000001&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x02&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00000010&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x03&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00000011&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x04&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00000100&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x05&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |0b00000101&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x06&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |0b00000110&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x07&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00000111&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x08&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00001000&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x09&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00001001&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x0A&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00001010&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 11&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x0B&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00001011&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 12&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x0C&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00001100&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 13&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x0D&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00001101&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 14&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x0E&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00001110&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 15&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x0F&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b00001111&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 100&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0x64&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b01100100&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0xFF&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | 0b11111111&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;0b&amp;quot; und &amp;quot;0x&amp;quot; haben für die Berechnung keine Bedeutung, sie zeigen nur an, dass es sich bei dieser Zahl um eine Binär- bzw. Hexadezimalzahl handelt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=FF0000&amp;gt;Wichtig dabei ist es, dass Hexadezimal- bzw. Binärzahlen bzw. Dezimalzahlen nur unterschiedliche Schreibweisen für immer das Gleiche sind: Eine Zahl. Welche Schreibweise bevorzugt wird, hängt auch vom Verwendungszweck ab. Je nachdem kann die eine oder die andere Schreibweise klarer sein.&amp;lt;/font&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;font color=FF0000&amp;gt;Auch noch sehr wichtig: Computer und µCs beginnen immer bei 0 zu zählen, d.h. wenn es 8 Dinge (Bits etc.) gibt, hat das erste die Nummer 0, das zweite die Nummer 1, ..., und das letzte (das 8.) die Nummer 7(!).&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Ausgabe ==&lt;br /&gt;
=== Assembler-Sourcecode ===&lt;br /&gt;
&lt;br /&gt;
Unser erstes Assemblerprogramm, das wir auf dem Controller laufen lassen möchten, sieht so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;         ; Definitionsdatei für den Prozessortyp einbinden&lt;br /&gt;
&lt;br /&gt;
         ldi r16, 0xFF       ; lade Arbeitsregister r16 mit der Konstanten 0xFF&lt;br /&gt;
         out DDRB, r16       ; Inhalt von r16 ins IO-Register DDRB ausgeben&lt;br /&gt;
&lt;br /&gt;
         ldi r16, 0b11111100 ; 0b11111100 in r16 laden&lt;br /&gt;
         out PORTB, r16      ; r16 ins IO-Register PORTB ausgeben&lt;br /&gt;
&lt;br /&gt;
ende:    rjmp ende           ; Sprung zur Marke &amp;quot;ende&amp;quot; -&amp;gt; Endlosschleife&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Assemblieren ===&lt;br /&gt;
Das Programm muss mit der Endung &amp;quot;.asm&amp;quot; abgespeichert werden, z.&amp;amp;nbsp;B. als &amp;quot;leds.asm&amp;quot;. Diese Datei können wir aber noch nicht direkt auf den Controller programmieren. Zuerst müssen wir sie dem Assembler füttern. Bei wavrasm funktioniert das z.&amp;amp;nbsp;B., indem wir ein neues Fenster öffnen, den Programmtext hineinkopieren, speichern und auf &amp;quot;assemble&amp;quot; klicken. Wichtig ist, dass sich die Datei &amp;quot;m8def.inc&amp;quot; (wird beim Atmel-Assembler mitgeliefert) im gleichen Verzeichnis wie die Assembler-Datei befindet. Der Assembler übersetzt die Klartext-Befehle des Assemblercodes in für den Mikrocontroller verständlichen Binärcode und gibt ihn in Form einer sogenannten &amp;quot;Hex-Datei&amp;quot; aus. Diese Datei kann man dann mit der entsprechenden Software direkt in den Controller programmieren. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid grey; padding: 1ex;&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hinweis: Konfigurieren der Taktversorgung des ATmega8 ===&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;ATmega8&#039;&#039;&#039; ist standardmäßig der interne 1 MHz-Oszillator aktiviert; weil dieser für viele Anwendungen (z.&amp;amp;nbsp;B. das UART, siehe späteres Kapitel) aber nicht genau genug ist, soll der Mikrocontroller seinen Takt aus dem angeschlossenen 4 MHz-Quarzoszillator beziehen. Dazu müssen ein paar Einstellungen an den &#039;&#039;&#039;Fusebits&#039;&#039;&#039; des Controllers vorgenommen werden. Am besten und sichersten geht das mit dem Programm [http://www.myplace.nu/avr/yaap/ yaap]. Wenn man das Programm gestartet hat und der ATmega8 richtig erkannt wurde, wählt man aus den Menüs den Punkt &amp;quot;Lock Bits &amp;amp; Fuses&amp;quot; und klickt zunächst auf &amp;quot;Read Fuses&amp;quot;. Das Ergebnis sollte so aussehen: [http://www.mikrocontroller.net/images/atmega8-vorher.png Screenshot]. Nun ändert man die Kreuze so, dass das folgende Bild entsteht: [http://www.mikrocontroller.net/images/atmega8-nachher.png Screenshot] und klickt auf &amp;quot;Write Fuses&amp;quot;. Vorsicht, wenn die Einstellungen nicht stimmen, kann es sein, dass die ISP-Programmierung deaktiviert wird und man den AVR somit nicht mehr programmieren kann! Die FuseBits bleiben übrigens nach dem Löschen des Controllers aktiv, müssen also nur ein einziges Mal eingestellt werden. Mehr über die Fuse-Bits findet sich im Artikel [[AVR Fuses]].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach dem Assemblieren sollte eine neue Datei mit dem Namen &amp;quot;leds.hex&amp;quot; oder &amp;quot;leds.rom&amp;quot; vorhanden sein, die man mit yaap, PonyProg oder AVRISP in den Flash-Speicher des Mikrocontrollers laden kann. Wenn alles geklappt hat, leuchten jetzt die ersten beiden angeschlossenen LEDs.&lt;br /&gt;
&lt;br /&gt;
=== Programmerklärung ===&lt;br /&gt;
In der ersten Zeile wird die Datei m8def.inc eingebunden, welche die prozessortypischen Bezeichnungen für die verschiedenen Register definiert. Wenn diese Datei fehlen würde, wüsste der Assembler nicht, was mit &amp;quot;PORTB&amp;quot;, &amp;quot;DDRD&amp;quot; usw. gemeint ist. Für jeden AVR-Mikrocontroller gibt es eine eigene derartige Include-Datei, da zwar die Registerbezeichnungen bei allen Controllern mehr oder weniger gleich sind, die Register aber auf unterschiedlichen Controllern unterschiedlich am Chip angeordnet sind und nicht alle Funktionsregister auf allen Prozessoren existieren. Für einen ATmega8 beispielsweise würde die einzubindende Datei m8def.inc heißen. Normalerweise ist also im Namen der Datei der Name des Chips in irgendeiner Form, auch abgekürzt, enthalten. Kennt man den korrekten Namen einmal nicht, so sieht man ganz einfach nach. Alle Include-Dateien wurden von Atmel in einem gemeinsamen Verzeichnis gespeichert. Das Verzeichnis ist bei einer Standardinstallation am PC auf C:\Programme\Atmel\AVR Tools\AvrAssembler\Appnotes\. Einige Include-Dateien heißen&lt;br /&gt;
&lt;br /&gt;
  AT90s2313:  2313def.inc&lt;br /&gt;
  ATmega8:    m8def.inc&lt;br /&gt;
  ATmega16:   m16def.inc&lt;br /&gt;
  ATmega32:   m32def.inc&lt;br /&gt;
  ATTiny12:   tn12def.inc&lt;br /&gt;
  ATTiny2313: tn2313def.inc&lt;br /&gt;
&lt;br /&gt;
Um sicher zu gehen, dass man die richtige Include-Datei hat, kann man diese mit einem Texteditor (AVR-Studio oder Notepad) öffnen. Der Name des Prozessors wurde von Atmel immer an den Anfang der Datei geschrieben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
;***************************************************************************&lt;br /&gt;
;* A P P L I C A T I O N   N O T E   F O R   T H E   A V R   F A M I L Y&lt;br /&gt;
;* &lt;br /&gt;
;* Number               :AVR000&lt;br /&gt;
;* File Name            :&amp;quot;2313def.inc&amp;quot;&lt;br /&gt;
;* Title                :Register/Bit Definitions for the AT90S2313&lt;br /&gt;
;* Date                 :99.01.28&lt;br /&gt;
;* Version              :1.30&lt;br /&gt;
;* Support E-Mail       :avr@atmel.com&lt;br /&gt;
;* Target MCU           :AT90S2313&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber jetzt weiter mit dem selbstgeschriebenen Programm.&lt;br /&gt;
&lt;br /&gt;
In der 2. Zeile wird mit dem Befehl &#039;&#039;&#039;ldi r16, 0xFF&#039;&#039;&#039; der Wert 0xFF (entspricht 0b11111111) in das Register r16 geladen (mehr Infos unter [[Adressierung]]). Die AVRs besitzen 32 Arbeitsregister, r0-r31, die als Zwischenspeicher zwischen den I/O-Registern (z.&amp;amp;nbsp;B. DDRB, PORTB, UDR...) und dem RAM genutzt werden. Zu beachten ist außerdem, dass die ersten 16 Register (r0-r15) nicht von jedem Assemblerbefehl genutzt werden können. Ein Register kann man sich als eine Speicherzelle direkt im Mikrocontroller vorstellen. Natürlich besitzt der Controller noch viel mehr Speicherzellen, die werden aber ausschließlich zum Abspeichern von Daten verwendet. Um diese Daten zu manipulieren, müssen sie zuerst in eines der Register geladen werden. Nur dort ist es möglich, die Daten zu manipulieren und zu verändern. Ein Register ist also vergleichbar mit einer Arbeitsfläche, während der restliche Speicher eher einem Stauraum entspricht. Will man arbeiten, so muss das Werkstück (= die Daten) aus dem Stauraum auf die Arbeitsfläche geholt werden und kann dann dort bearbeitet werden.&lt;br /&gt;
Der Befehl Befehl &#039;&#039;&#039;ldi&#039;&#039;&#039; lädt jetzt einen bestimmten konstanten Wert in so ein Arbeitsregister. In diesem Fall kommt der zu ladende Wert also nicht aus dem Stauraum, sondern der Programmierer kennt ihn bereits. Auch Assemblerbefehle sind nicht einfach willkürlich gewählte Buchstabenkombinationen, sondern sind oft Abkürzungen für eine bestimmte Aktion. &#039;&#039;&#039;ldi&#039;&#039;&#039; bedeutet in Langform &#039;&#039;&#039;Load immediate&#039;&#039;&#039;. Load ist klar - laden. Und immediate bedutet, dass der zu ladende Wert beim Befehl selber angegeben wurde (engl. immediate - unmittelbar).&lt;br /&gt;
&lt;br /&gt;
Die Erklärungen nach dem Semikolon sind Kommentare und werden vom Assembler nicht beachtet.&lt;br /&gt;
&lt;br /&gt;
Der 3. Befehl, &#039;&#039;&#039;out&#039;&#039;&#039;, gibt den Inhalt von r16 (=0xFF) in das Datenrichtungsregister für Port B aus. Das Datenrichtungsregister legt fest, welche Portpins als Ausgang und welche als Eingang genutzt werden. Steht in diesem Register ein Bit auf 0, wird der entsprechende Pin als Eingang konfiguriert, steht es auf 1, ist der Pin ein Ausgang. In diesem Fall sind also alle 6 Pins von Port B Ausgänge. Datenrichtungsregister können ebenfalls nicht direkt beschrieben werden, daher muss man den Umweg über eines der normalen Register r16 - r31 gehen.&lt;br /&gt;
&lt;br /&gt;
Der nächste Befehl, &#039;&#039;&#039;ldi r16, 0b11111100&#039;&#039;&#039; lädt den Wert 0b11111100 in das Arbeitsregister r16, der durch den darauffolgenden Befehl &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; in das I/O-Register PORTB (und damit an den Port, an dem die LEDs angeschlossen sind) ausgegeben wird. Eine 1 im PORTB-Register bedeutet, dass an dem entsprechenden Anschluss des Controllers die Spannung 5V anliegt, bei einer 0 sind es 0V (Masse). &lt;br /&gt;
&lt;br /&gt;
Schließlich wird mit &#039;&#039;&#039;rjmp ende&#039;&#039;&#039; ein Sprung zur Marke &#039;&#039;&#039;ende:&#039;&#039;&#039; ausgelöst, also an die gleiche Stelle, wodurch eine Endlosschleife entsteht. Sprungmarken schreibt man gewöhnlich an den Anfang der Zeile, Befehle in die 2. und Kommentare in die 3. Spalte. Ein Marke ist einfach nur ein symbolischer Name, auf den man sich in Befehlen beziehen kann. Sie steht stellvertretend für die Speicheradresse des unmittelbar folgenden Befehls. Der Assembler, der den geschriebenen Text in eine für den µC ausführbare Form bringt, führt über die Marken Buch und ersetzt in den eigentlichen Befehlen die Referenzierungen auf die Marken mit den korrekten Speicheradressen.&lt;br /&gt;
&lt;br /&gt;
Bei Kopier- und Ladebefehlen (ldi, in, out...) wird immer der 2. Operand in den ersten kopiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
         ldi r17, 15     ; das Register r17 wird mit der Konstanten 15 geladen&lt;br /&gt;
         mov r16, r17    ; das Register r16 wird mit dem Inhalt des Registers r17 geladen&lt;br /&gt;
         out PORTB, r16  ; das IO-Register &amp;quot;PORTB&amp;quot; wird mit dem Inhalt des Registers r16 geladen&lt;br /&gt;
         in r16, PIND    ; das Register 16 wird mit dem Inhalt des IO-Registers &amp;quot;PIND&amp;quot; geladen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wer mehr über die Befehle wissen möchte, sollte sich die PDF-Datei [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf Instruction Set (1,27 MB)] runterladen (benötigt [http://www.acrobat.com/ Acrobat Reader] oder in der Hilfe von Assembler oder AVR-Studio nachschauen. Achtung: nicht alle Befehle sind auf jedem Controller der AVR-Serie verwendbar! &lt;br /&gt;
&lt;br /&gt;
Nun sollten die beiden ersten LEDs leuchten, weil die Portpins PB0 und PB1 durch die Ausgabe von 0 (low) auf Masse (0V) gelegt werden und somit ein Strom durch die gegen Vcc (5V) geschalteten LEDs fließen kann. Die 4 anderen LEDs sind aus, da die entsprechenden Pins durch die Ausgabe von 1 (high) auf 5V liegen. &lt;br /&gt;
&lt;br /&gt;
Warum leuchten die beiden ersten LEDs, wo doch die beiden letzen Bits auf 0 gesetzt sind? Das liegt daran, dass man die Bitzahlen von rechts nach links schreibt. Ganz rechts steht das niedrigstwertige Bit (&amp;quot;LSB&amp;quot;, Least Significant Bit), das man als Bit 0 bezeichnet, und ganz links das höchstwertige Bit (&amp;quot;MSB&amp;quot;, Most Significant Bit), bzw. Bit 7. Das Prefix &amp;quot;0b&amp;quot; gehört nicht zur Zahl, sondern sagt dem Assembler, dass die nachfolgende Zahl in binärer Form interpretiert werden soll.&lt;br /&gt;
&lt;br /&gt;
http://www.mikrocontroller.net/images/bits.gif&lt;br /&gt;
&lt;br /&gt;
Das LSB steht für PB0, und das MSB für PB7... aber PB7 gibt es doch z.&amp;amp;nbsp;B. beim AT90S4433 gar nicht, es geht doch nur bis PB5? Der Grund ist einfach: Am Gehäuse des AT90S4433 gibt es nicht genug Pins für den kompletten Port B, deshalb existieren die beiden obersten Bits nur intern.&lt;br /&gt;
&lt;br /&gt;
== Eingabe ==&lt;br /&gt;
Im folgenden Programm wird Port B als Ausgang und Port D als Eingang verwendet: &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/leds+buttons.asm Download leds+buttons.asm]&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
         ldi r16, 0xFF&lt;br /&gt;
         out DDRB, r16     ; Alle Pins am Port B durch Ausgabe von 0xFF ins&lt;br /&gt;
                           ; Richtungsregister DDRB als Ausgang konfigurieren&lt;br /&gt;
         ldi r16, 0x00&lt;br /&gt;
         out DDRD, r16     ; Alle Pins am Port D durch Ausgabe von 0x00 ins&lt;br /&gt;
                           ; Richtungsregister DDRD als Eingang konfigurieren&lt;br /&gt;
loop:&lt;br /&gt;
         in r16, PIND      ; an Port D anliegende Werte (Taster) nach r16 einlesen&lt;br /&gt;
         out PORTB, r16    ; Inhalt von r16 an Port B ausgeben&lt;br /&gt;
         rjmp loop         ; Sprung zu &amp;quot;loop:&amp;quot; -&amp;gt; Endlosschleife&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn der Port D als Eingang geschaltet ist, können die anliegenden Daten über das IO-Register &#039;&#039;&#039;PIND&#039;&#039;&#039; eingelesen werden. Dazu wird der Befehl &#039;&#039;&#039;in&#039;&#039;&#039; verwendet, der ein IO-Register (in diesem Fall PIND) in ein Arbeitsregister (z.&amp;amp;nbsp;B. r16) kopiert. Danach wird der Inhalt von r16 mit dem Befehl &#039;&#039;&#039;out&#039;&#039;&#039; an Port B ausgegeben. Dieser Umweg ist notwendig, da man nicht direkt von einem IO-Register in ein anderes kopieren kann. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;rjmp loop&#039;&#039;&#039; sorgt dafür, dass die Befehle &#039;&#039;&#039;in r16, PIND&#039;&#039;&#039; und &#039;&#039;&#039;out PORTB, r16&#039;&#039;&#039; andauernd wiederholt werden, so dass immer die zu den gedrückten Tasten passenden LEDs leuchten.&lt;br /&gt;
&lt;br /&gt;
Achtung: Auch wenn es hier nicht explizit erwähnt wird: Man kann natürlich jeden Pin eines jeden Ports einzeln auf Ein- oder Ausgabe schalten. Dass hier ein kompletter Port jeweils als Eingabe bzw. Ausgabe benutzt wurde, ist reine Bequemlichkeit.&lt;br /&gt;
&lt;br /&gt;
=== mögliche Zeitverzögerungen ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; In bestimmten Situationen kann es passieren, dass scheinbar Pins nicht richtig gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Speziell bei der Abfrage von Matrixtastaturen kann der Effekt auftreten, dass Tasten scheinbar nicht reagieren. Typische Sequenzen sehen dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi r16,0x0F      &lt;br /&gt;
        out DDRD,r16    ; oberes Nibble Eingang, unteres Ausgang&lt;br /&gt;
        ldi r16,0xFE     &lt;br /&gt;
        out PORTD,r16   ; PD0 auf 0 ziehen, PD4..7 Pull ups aktiv&lt;br /&gt;
        in  r17,PIND    ; Pins lesen schlägt hier fehl!&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warum ist das problematisch? Nun, der AVR ist ein RISC-Microcontroller, welcher die meisten Befehle in einem Takt ausführt. Gleichzeitig werden aber alle Eingangssignale über FlipFlops abgetastet (synchronisiert), damit sie sauber im AVR zur Verfügung stehen. Dadurch ergibt sich eine Verzögerung (Latenz) von bis zu 1,5 Takten, mit der auf externe Signale reagiert werden kann. Die Erklärung dazu findet man im Datenblatt unter der Überschrift &amp;quot;I/O Ports - Reading the Pin Value&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Was tun? Wenn der Wert einer Port-Eingabe von einer unmittelbar vorangehenden Port-Ausgabe abhängt, muss man wenigstens einen weiteren Befehl zwischen beiden einfügen, im einfachsten Fall ein &#039;&#039;&#039;nop&#039;&#039;&#039;. &#039;&#039;&#039;nop&#039;&#039;&#039; bedeutet in Langform &#039;&#039;&#039;no operation&#039;&#039;&#039;, und genau das macht der Befehl auch: nichts. Er dient einzig und alleine dazu, dass der Prozessor einen Befehl abarbeitet, also etwas zu tun hat, aber ansonsten an den Registern oder sonstigen Internals nichts verändert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        ldi r16,0x0F&lt;br /&gt;
        out DDRD,r16    ; oberes Nibble Eingang, unteres Ausgang&lt;br /&gt;
        ldi r16,0xFE&lt;br /&gt;
        out PORTD,r16   ; PD0 auf 0 ziehem, PD4..7 Pull ups aktiv&lt;br /&gt;
        NOP             ; Delay der Synchronisations-FlipFlops ausgleichen&lt;br /&gt;
        in  r17,PIND    ; Pins lesen ist hier OK.&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiteres Beispiel für dieses Verhalten bei rasch aufeinanderfolgenden &#039;&#039;&#039;out&#039;&#039;&#039; und &#039;&#039;&#039;in&#039;&#039;&#039; Anweisungen ist in einem Forenbeitrag zur [http://www.mikrocontroller.net/topic/88680#753847 Abfrage des Busyflag bei einem LCD] angegeben. Dort spielen allerdings weitere, vom [[LCD]]-Controller abhängige Timings eine wesentliche Rolle für den korrekten Programmablauf.&lt;br /&gt;
&lt;br /&gt;
==Pullup-Widerstand==&lt;br /&gt;
[[Bild:Taster.gif|framed|right| Standard Taster Anschluss]]&lt;br /&gt;
&lt;br /&gt;
Bei der Besprechung der notwendigen Beschaltung der Ports wurde an einen Eingangspin jeweils ein Taster mit einem Widerstand nach Vcc vorgeschlagen. Diesen Widerstand nennt man einen &#039;&#039;&#039;Pullup&#039;&#039;&#039;-Widerstand. Wenn der Taster geöffnet ist, so ist es seine Aufgabe, den Eingangspegel am Pin auf Vcc zu ziehen. Daher auch der Name: &#039;pull up&#039; (engl. für hochziehen). Ohne diesen Pullup-Widerstand würde ansonsten der Pin bei geöffnetem Taster in der Luft hängen, also weder mit Vcc noch mit GND verbunden sein. Dieser Zustand ist aber unbedingt zu vermeiden, da bereits elektromagnetische Einstreuungen auf Zuleitungen ausreichen, dem Pin einen Zustand vorzugaukeln, der in Wirklichkeit nicht existiert. Der Pullup-Widerstand sorgt also für einen definierten 1-Pegel bei geöffnetem Taster. Wird der Taster geschlossen, so stellt dieser eine direkte Verbindung zu GND her und der Pegel am Pin fällt auf GND. Durch den Pullup-Widerstand rinnt dann ein kleiner Strom von Vcc nach GND. Da Pullup-Widerstände in der Regel aber relativ hochohmig sind, stört dieser kleine Strom meistens nicht weiter.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Taster2.gif|framed|right| Taster bei Benutzung des interen Pullup]]&lt;br /&gt;
&lt;br /&gt;
Anstelle eines externen Widerstandes wäre es auch möglich, den Widerstand wegzulassen und stattdessen den in den AVR eingebauten Pullup-Widerstand zu aktivieren. Die Beschaltung eines Tasters vereinfacht sich dann zum einfachst möglichen Fall: Der Taster wird direkt an den Eingangspin des µC angeschlossen und schaltet nach Masse durch.&lt;br /&gt;
&lt;br /&gt;
Das geht allerdings nur dann, wenn der entsprechende Mikroprozessor-Pin auf Eingang geschaltet wurde. Ein Pullup-Widerstand hat nun mal nur bei einem Eingangspin einen Sinn. Bei einem auf Ausgang geschalteten Pin sorgt der Mikroprozessor dafür, dass ein dem Port-Wert entsprechender Spannungspegel ausgegeben wird. Ein Pullup-Widerstand wäre in so einem Fall kontraproduktiv, da der Widerstand versucht, den Pegel am Pin auf Vcc zu ziehen, während eine 0 im Port-Register dafür sorgt, dass der Mikroprozessor versuchen würde, den Pin auf GND zu ziehen.&lt;br /&gt;
&lt;br /&gt;
Ein Pullup-Widerstand an einem Eingangspin wird durch das &#039;&#039;&#039;PORT&#039;&#039;&#039;-Register gesteuert. Das &#039;&#039;&#039;PORT&#039;&#039;&#039;-Register erfüllt also 2 Aufgaben. Bei einem auf Ausgang geschalteten Port steuert es den Pegel an den Ausgangspins. Bei einem auf Eingang geschalteten Port steuert es, ob die internen Pullup-Widerstände aktiviert werden oder nicht. Ein 1-Bit aktiviert den entsprechenden Pullup-Widerstand.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}}&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! DDRx   || PORTx  ||  IO-Pin-Zustand&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
|| Eingang ohne Pull-Up (Resetzustand)&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|| Eingang mit Pull-Up&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
|| [[Ausgangsstufen_Logik-ICs#Push-Pull | Push-Pull]]-Ausgang auf LOW&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|| [[Ausgangsstufen_Logik-ICs#Push-Pull | Push-Pull]]-Ausgang auf HIGH&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
         ldi r16, 0xFF&lt;br /&gt;
         out DDRB, r16     ; Alle Pins am Port B durch Ausgabe von 0xFF ins&lt;br /&gt;
                           ; Richtungsregister DDRB als Ausgang konfigurieren&lt;br /&gt;
         ldi r16, 0x00&lt;br /&gt;
         out DDRD, r16     ; Alle Pins am Port D durch Ausgabe von 0x00 ins&lt;br /&gt;
                           ; Richtungsregister DDRD als Eingang konfigurieren&lt;br /&gt;
&lt;br /&gt;
         ldi r16, 0xFF     ; An allen Pins vom Port D die Pullup-Widerstände&lt;br /&gt;
         out PORTD, r16    ; aktivieren. Dies geht deshalb durch eine Ausgabe&lt;br /&gt;
                           ; nach PORTD, da ja der Port auf Eingang gestellt ist.&lt;br /&gt;
loop:&lt;br /&gt;
         in r16, PIND      ; an Port D anliegende Werte (Taster) nach r16 einlesen&lt;br /&gt;
         out PORTB, r16    ; Inhalt von r16 an Port B ausgeben&lt;br /&gt;
         rjmp loop         ;  zu &amp;quot;loop:&amp;quot; -&amp;gt; Endlosschleife&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Werden auf diese Art und Weise die AVR-internen Pullup-Widerstände aktiviert, so sind keine externen Widerstände mehr notwendig und die Beschaltung vereinfacht sich zu einem Taster, der einfach nur den µC-Pin mit GND verbindet.&lt;br /&gt;
&lt;br /&gt;
==Zugriff auf einzelne Bits==&lt;br /&gt;
Man muss nicht immer ein ganzes Register auf einmal einlesen oder mit einem neuen Wert laden. Es gibt auch Befehle, mit denen man einzelne Bits abfragen und ändern kann: &lt;br /&gt;
&lt;br /&gt;
* Der Befehl &#039;&#039;&#039;sbic&#039;&#039;&#039; (&amp;quot;skip if bit cleared&amp;quot;) überspringt den darauffolgenden Befehl, wenn das angegebene Bit 0 (low) ist.&lt;br /&gt;
* &#039;&#039;&#039;sbis&#039;&#039;&#039; (&amp;quot;skip if bit set&amp;quot;) bewirkt das Gleiche, wenn das Bit 1 (high) ist.&lt;br /&gt;
* Mit &#039;&#039;&#039;cbi&#039;&#039;&#039; (&amp;quot;clear bit&amp;quot;) wird das angegebene Bit auf 0 gesetzt.&lt;br /&gt;
* &#039;&#039;&#039;sbi&#039;&#039;&#039; (&amp;quot;set bit&amp;quot;) bewirkt das Gegenteil.&lt;br /&gt;
Achtung: Diese Befehle können nur auf die IO-Register angewandt werden! &lt;br /&gt;
&lt;br /&gt;
Am besten verstehen kann man das natürlich an einem Beispiel: &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/bitaccess.asm Download bitaccess.asm]&lt;br /&gt;
&amp;lt;avrasm&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
         ldi r16, 0xFF&lt;br /&gt;
         out DDRB, r16       ; Port B ist Ausgang&lt;br /&gt;
                             &lt;br /&gt;
         ldi r16, 0x00&lt;br /&gt;
         out DDRD, r16       ; Port D ist Eingang&lt;br /&gt;
                             &lt;br /&gt;
&lt;br /&gt;
         ldi r16, 0xFF&lt;br /&gt;
         out PORTB, r16      ; PORTB auf 0xFF setzen -&amp;gt; alle LEDs aus&lt;br /&gt;
&lt;br /&gt;
loop:    sbic PIND, 0        ; &amp;quot;skip if bit cleared&amp;quot;, nächsten Befehl überspringen,&lt;br /&gt;
                             ; wenn Bit 0 im IO-Register PIND =0 (Taste 0 gedrückt)&lt;br /&gt;
         rjmp loop           ; Sprung zu &amp;quot;loop:&amp;quot; -&amp;gt; Endlosschleife&lt;br /&gt;
&lt;br /&gt;
         cbi PORTB, 3        ; Bit 3 im IO-Register PORTB auf 0 setzen -&amp;gt; 4. LED an&lt;br /&gt;
&lt;br /&gt;
ende:    rjmp ende           ; Endlosschleife&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses Programm wartet so lange in einer Schleife (&amp;quot;&#039;&#039;&#039;loop:&#039;&#039;&#039;&amp;quot;...&amp;quot;&#039;&#039;&#039;rjmp loop&#039;&#039;&#039;&amp;quot;), bis Bit 0 im Register &#039;&#039;&#039;PIND&#039;&#039;&#039; 0 wird, also die erste Taste gedrückt ist. Durch &#039;&#039;&#039;sbic&#039;&#039;&#039; wird dann der Sprungbefehl zu &amp;quot;&#039;&#039;&#039;loop:&#039;&#039;&#039;&amp;quot; übersprungen, die Schleife wird also verlassen und das Programm danach fortgesetzt. Ganz am Ende schließlich wird das Programm durch eine leere Endlosschleife praktisch &amp;quot;angehalten&amp;quot;, da es ansonsten wieder von vorne beginnen würde.&lt;br /&gt;
&lt;br /&gt;
==Zusammenfassung der Portregister==&lt;br /&gt;
Für jeden Hardwareport gibt es im Mikroprozessor insgesamt 3 Register:&lt;br /&gt;
* Das Datenrichtungsregister &#039;&#039;&#039;DDRx&#039;&#039;&#039;. Es wird verwendet um die Richtung jedes einzelnen Mikroprozessor-Pins festzulegen. Eine 1 an der entsprechenden Bit Position steht für Ausgang, eine 0 steht für Eingang.&lt;br /&gt;
* Das Einleseregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Es wird verwendet um von einem Mikroprozessor-Pin den aktuellen, extern anliegenden Zustand einzulesen. Dazu muss das entsprechende Datenrichtungsbit auf Eingang geschaltet sein.&lt;br /&gt;
* Das Ausgangsregister &#039;&#039;&#039;PORTx&#039;&#039;&#039;. Es erfüllt 2 Funktionen, je nachdem wie das zugehörige Datenrichtungsbit geschaltet ist.&lt;br /&gt;
** Steht es auf Ausgang, so wird bei einer entsprechenden Zuweisung an das &#039;&#039;&#039;PORTx&#039;&#039;&#039; Register der entsprechende Mikroprozessor-Pin auf den angegebenen Wert gesetzt.&lt;br /&gt;
** Steht es auf Eingang, so beeinflusst das &#039;&#039;&#039;PORTx&#039;&#039;&#039;-Bit den internen Pullup-Widerstand an diesem Mikroprozessor-Pin. Bei einer 0 wird der Widerstand abgeschaltet, bei einer 1 wird der Widerstand an den Eingangs-Pin zugeschaltet.&lt;br /&gt;
* Bei den neueren AVR (wie z.&amp;amp;nbsp;B. &#039;&#039;ATtiny13&#039;&#039;, &#039;&#039;ATtiny2313&#039;&#039;, &#039;&#039;ATtiny24/44/84&#039;&#039;, &#039;&#039;ATtiny25/45/85&#039;&#039;, &#039;&#039;ATmega48/88/168&#039;&#039;, usw.) kann man als Ausgang konfigurierte Pins toggeln (&#039;&#039;&#039;PORTx&#039;&#039;&#039; zwischen 0 und 1 „umschalten“), indem man eine 1 an die entsprechende Bit Position des &#039;&#039;&#039;PINx&#039;&#039;&#039; Register schreibt.&lt;br /&gt;
&lt;br /&gt;
==Ausgänge benutzen, wenn mehr Strom benötigt wird==&lt;br /&gt;
Man kann nicht jeden beliebigen Verbraucher nach dem LED-Vorbild von oben an einen µC anschließen. Die Ausgänge des ATMega8 können nur eine begrenzte Menge Strom liefern, so dass der Chip schnell überfordert ist, wenn eine nachgeschaltete Schaltung mehr Strom benötigt. Die Ausgangstreiber des µC würden in solchen Fällen den Dienst quittieren und durchbrennen.&lt;br /&gt;
&lt;br /&gt;
Abhilfe schafft in solchen Fällen eine zusätzliche Treiberstufe, die im einfachsten Fall mit einem [[Transistor|Transistor]] als Schalter aufgebaut wird.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Transi2.gif|framed|center|Transistor Treiberstufe]]&lt;br /&gt;
&lt;br /&gt;
Die LED samt zugehörigen Widerständen dienen hier lediglich als Sinnbild für den Verbraucher, der vom µC ein und ausgeschaltet werden soll. Welcher Transistor als Schalter benutzt werden kann, hängt vom Stromverbrauch des Verbrauchers ab. Die Widerstände &#039;&#039;&#039;R1&#039;&#039;&#039; und &#039;&#039;&#039;R2&#039;&#039;&#039; werden als &#039;&#039;&#039;Basiswiderstände&#039;&#039;&#039; der Transistoren bezeichnet. Für ihre Berechnung siehe z.&amp;amp;nbsp;B. [[Basiswiderstand|hier]]. Um eine sichere Störfestigkeit im Resetfall des Mikrocontrollers zu gewähren (wenn der µC daher die Ausgänge noch nicht ansteuert), sollte man noch einen Pulldown Widerstand zwischen Basis und Emitter schalten oder einen digitalen Transistor (z.&amp;amp;nbsp;B. BCR135) mit integriertem Basis- und Basisemitterwiderstand benutzen.&lt;br /&gt;
&lt;br /&gt;
Um ein Relais an einen µC-Ausgang anzuschließen, siehe [[Relais_mit_Logik_ansteuern|hier]].&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Benötigte Ausrüstung|&lt;br /&gt;
zurücklink=AVR-Tutorial: Equipment|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=logische Operationen|&lt;br /&gt;
vorlink=AVR-Tutorial: Logik}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|IO-Grundlagen]]&lt;/div&gt;</summary>
		<author><name>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=62978</id>
		<title>AVR-Tutorial: LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_LCD&amp;diff=62978"/>
		<updated>2012-01-02T20:23:23Z</updated>

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

		<summary type="html">&lt;p&gt;Mkleemann: /* Weblinks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In weiterer Folge werden immer wieder 4 logische Grundoperationen auftauchen:&lt;br /&gt;
* UND&lt;br /&gt;
* ODER&lt;br /&gt;
* NICHT&lt;br /&gt;
* XOR (Exlusiv oder)&lt;br /&gt;
&lt;br /&gt;
Was hat es mit diesen Operationen auf sich?&lt;br /&gt;
&lt;br /&gt;
==Allgemeines==&lt;br /&gt;
Die logischen Operatoren werden mit einem Register und einem zweiten Argument gebildet. Das zweite Argument kann ebenfalls ein Register oder aber eine direkt angegebene Zahl sein.&lt;br /&gt;
&lt;br /&gt;
Da ein Register aus 8 Bit besteht, werden die logischen Operatoren immer auf alle 8 Bit Paare gleichzeitig angewendet.&lt;br /&gt;
&lt;br /&gt;
Mit den logischen Grundoperationen werden die beiden Argumente miteinander verknüpft und das Ergebnis der Verknüpfung im Register des ersten Argumentes abgelegt.&lt;br /&gt;
&lt;br /&gt;
==Die Operatoren==&lt;br /&gt;
===UND===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle|min-width:20em;text-align:center;}}&lt;br /&gt;
|+ &#039;&#039;&#039;Wahrheitstabelle&amp;amp;nbsp;UND&#039;&#039;&#039;&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!width=&amp;quot;30%&amp;quot;| A ||width=&amp;quot;30%&amp;quot;| B ||width=&amp;quot;30%&amp;quot;| Ergebnis&lt;br /&gt;
|-&lt;br /&gt;
|  0 || 0 ||  0&lt;br /&gt;
|-&lt;br /&gt;
|  0 || 1 ||  0&lt;br /&gt;
|-&lt;br /&gt;
|  1 || 0 ||  0&lt;br /&gt;
|-&lt;br /&gt;
|  1 || 1 ||  1&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis ist genau dann 1, wenn A &amp;lt;b&amp;gt;und&amp;lt;/b&amp;gt; B 1 sind&lt;br /&gt;
&lt;br /&gt;
====Verwendung====&lt;br /&gt;
* gezielt einzelne Bits auf 0 setzen&lt;br /&gt;
* dadurch auch die Verwendung um einzelne Bits auszumaskieren&lt;br /&gt;
&lt;br /&gt;
====AVR Befehle====&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        and  r16, r17             ; Verknüpfung zweier Register&lt;br /&gt;
        andi r16, 0b01011010      ; Verknüpfung eines Registers mit einer Konstanten&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die beiden Operanden werden miteinander &amp;lt;b&amp;gt;UND&amp;lt;/b&amp;gt; verknüpft, wobei jeweils die gleichwertigen Bits der Operanden laut Wahrheitstabelle unabhängig voneinander verknüpft werden.&lt;br /&gt;
&lt;br /&gt;
Sei der Inhalt des Registers r16 = 0b11001100, so lautet die Verknüpfung&lt;br /&gt;
&amp;lt;b&amp;gt;andi r16, 0b01011010&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
       0b11001100&lt;br /&gt;
       0b01011010   und&lt;br /&gt;
      -----------&lt;br /&gt;
       0b01001000&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird im ersten Operanden (r16) abgelegt.&lt;br /&gt;
&lt;br /&gt;
Im Ergebnis haben nur diejenigen Bits denselben Wert den sie im ersten Argument hatten, bei denen im zweiten Argument (in der Maske) eine 1 war. Alle anderen Bits sind auf jeden Fall 0.&lt;br /&gt;
Da in der Maske&lt;br /&gt;
      0b01011010&lt;br /&gt;
die Bits 0, 2, 5, 7 eine 0 aufweisen, ist auch im Ergebnis an diesen Stellen mit Sicherheit eine 0. Alle andern Bits (diejenigen bei denen in der Maske eine 1 steht), werden aus der Ursprungszahl so wie sie sind übernommen.&lt;br /&gt;
&lt;br /&gt;
===ODER===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle|min-width:20em;text-align:center;}}&lt;br /&gt;
|+ &#039;&#039;&#039;Wahrheitstabelle&amp;amp;nbsp;ODER&#039;&#039;&#039;&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!width=&amp;quot;30%&amp;quot;| A ||width=&amp;quot;30%&amp;quot;| B ||width=&amp;quot;30%&amp;quot;| Ergebnis&lt;br /&gt;
|-&lt;br /&gt;
|  0 || 0 ||  0&lt;br /&gt;
|-&lt;br /&gt;
|  1 || 0 ||  1&lt;br /&gt;
|-&lt;br /&gt;
|  0 || 1 ||  1&lt;br /&gt;
|-&lt;br /&gt;
|  1 || 1 ||  1&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis ist genau dann 1, wenn A &amp;lt;b&amp;gt;oder&amp;lt;/b&amp;gt; B &amp;lt;b&amp;gt;oder beide&amp;lt;/b&amp;gt; 1 sind.&lt;br /&gt;
&lt;br /&gt;
====Verwendung====&lt;br /&gt;
* gezielt einzelne Bits auf 1 setzen&lt;br /&gt;
&lt;br /&gt;
====AVR Befehle====&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        or   r16, r17           ; Verknüpfung zweier Register&lt;br /&gt;
        ori  r16, 0b01011010    ; Verknüpfung eines Registers mit einer Konstanten&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die beiden Operanden werden miteinander &amp;lt;b&amp;gt;ODER&amp;lt;/b&amp;gt; verknüpft, wobei jeweils die jeweils gleichwertigen Bits der Operanden laut Wahrheitstabelle unabhängig voneinander verknüpft werden.&lt;br /&gt;
&lt;br /&gt;
Sei der Inhalt des Registers r16 = 0b11001100, so lautet die Verknüpfung&lt;br /&gt;
&amp;lt;b&amp;gt;ori  r16, 0b01011010&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
       0b11001100&lt;br /&gt;
       0b01011010     oder&lt;br /&gt;
      -----------&lt;br /&gt;
       0b11011110&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird im ersten Operanden (r16) abgelegt.&lt;br /&gt;
&lt;br /&gt;
Im Ergebnis tauchen an den Bitpositionen an denen in der Maske eine 1 war auf jeden Fall ebenfalls eine 1 auf. In den restlichen Bitpositionen hängt es vom ersten Argument ab, ob im Ergebnis eine 1 auftaucht oder nicht.&lt;br /&gt;
&lt;br /&gt;
Da in der Maske&lt;br /&gt;
       0b01011010&lt;br /&gt;
an den Bitpositionen 1, 3, 4, 6 eine 1 steht, ist an diesen Bitpositionen im Ergebnis ebenfalls mit Sicherheit eine 1. Alle andern Bits werden so wie sie sind aus der Ursprungszahl übernommen.&lt;br /&gt;
&lt;br /&gt;
===NICHT===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle|min-width:15em;text-align:center;}}&lt;br /&gt;
|+ &#039;&#039;&#039;Wahrheitstabelle&amp;amp;nbsp;NICHT&#039;&#039;&#039;&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!width=&amp;quot;50%&amp;quot;| A ||width=&amp;quot;50%&amp;quot;| Ergebnis&lt;br /&gt;
|-&lt;br /&gt;
|  0 || 1&lt;br /&gt;
|-&lt;br /&gt;
|  1 || 0&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis ist genau dann 1, wenn A &amp;lt;b&amp;gt;nicht&amp;lt;/b&amp;gt; 1 ist.&lt;br /&gt;
&lt;br /&gt;
====Verwendung====&lt;br /&gt;
* alle Bits eines Bytes umdrehen&lt;br /&gt;
&lt;br /&gt;
====AVR Befehle====&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        com  r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sei der Inhalt des Registers r16 = 0b11001100, so lautet die Verknüpfung&lt;br /&gt;
&amp;lt;b&amp;gt;com  r16&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
       0b11001100     nicht&lt;br /&gt;
      -----------&lt;br /&gt;
       0b00110011&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird im ersten und einzigen Operanden (r16) abgelegt.&lt;br /&gt;
&lt;br /&gt;
===XOR (Exlusives Oder)===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle|min-width:20em;text-align:center;}}&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Wahrheitstabelle&amp;amp;nbsp;XOR&#039;&#039;&#039;&lt;br /&gt;
!width=&amp;quot;30%&amp;quot;| A ||width=&amp;quot;30%&amp;quot;| B ||width=&amp;quot;30%&amp;quot;| Ergebnis&lt;br /&gt;
|- &lt;br /&gt;
|  0 ||  0 ||  0&lt;br /&gt;
|- &lt;br /&gt;
|  0 ||  1 ||  1&lt;br /&gt;
|- &lt;br /&gt;
|  1 ||  0 ||  1&lt;br /&gt;
|- &lt;br /&gt;
|  1 ||  1 ||  0&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis ist genau dann 1, wenn A &amp;lt;b&amp;gt;oder&amp;lt;/b&amp;gt; B, aber &amp;lt;b&amp;gt;nicht beide&amp;lt;/b&amp;gt; 1 sind.&lt;br /&gt;
&lt;br /&gt;
====Verwendung====&lt;br /&gt;
* gezielt einzelne Bits umdrehen&lt;br /&gt;
&lt;br /&gt;
====AVR Befehle====&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        eor  r16, r17           ; Verknüpfung zweier Register&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die beiden Operanden werden miteinander &amp;lt;b&amp;gt;XOR&amp;lt;/b&amp;gt; verknüpft, wobei jeweils die jeweils gleichwertigen Bits der Operanden laut Wahrheitstabelle unabhängig voneinander verknüpft werden.&lt;br /&gt;
&lt;br /&gt;
Sei der Inhalt des Registers r16 = 0b11001100 und der Inhalt des Registers r17 = 0b01011010, so lautet die Verknüpfung &amp;lt;b&amp;gt;eor r16, r17&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
       0b11001100&lt;br /&gt;
       0b01011010     xor&lt;br /&gt;
      -----------&lt;br /&gt;
       0b10010110&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis wird im ersten Operanden (r16) abgelegt.&lt;br /&gt;
&lt;br /&gt;
Im Ergebnis werden diejenigen Bits umgedreht, an deren Bitposition in der Maske eine 1 vorhanden ist.&lt;br /&gt;
&lt;br /&gt;
Da in der Maske&lt;br /&gt;
       0b01011010&lt;br /&gt;
an den Bitpositionen 1, 3, 4, 6 jeweils eine 1 steht, enthält das Ergebnis an eben diesen Bitpositionen die umgedrehten Bits aus der Ursprungszahl. Alle anderen Bits werden so wie sie sind aus der Ursprungszahl übernommen.&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
*[http://www.quinndunki.com/gate/ GATE: The Logic Game] - Eine kleine Abwechselung für die Tutorialpause. (ab Windows XP, Mac OS X)&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=I/O Grundlagen|&lt;br /&gt;
zurücklink=AVR-Tutorial: IO-Grundlagen|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Arithmetik|&lt;br /&gt;
vorlink=AVR-Tutorial: Arithmetik8}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Logik]]&lt;/div&gt;</summary>
		<author><name>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Mehrfachverzweigung&amp;diff=62976</id>
		<title>AVR-Tutorial: Mehrfachverzweigung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Mehrfachverzweigung&amp;diff=62976"/>
		<updated>2012-01-02T20:22:43Z</updated>

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

		<summary type="html">&lt;p&gt;Mkleemann: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;PWM - Dieses Kürzel steht für &#039;&#039;&#039;P&#039;&#039;&#039;uls &#039;&#039;&#039;W&#039;&#039;&#039;eiten &#039;&#039;&#039;M&#039;&#039;&#039;odulation.&lt;br /&gt;
&lt;br /&gt;
==Was bedeutet PWM?==&lt;br /&gt;
Viele elektrische Verbraucher können in ihrer Leistung reguliert werden, indem die Versorgungsspannung in weiten Bereichen verändert wird. Ein normaler Gleichstrommotor wird z.&amp;amp;nbsp;B. langsamer laufen, wenn er mit einer geringeren Spannung versorgt wird, bzw. schneller laufen, wenn er mit einer höheren Spannung versorgt wird. LEDs werden zwar nicht mit einer Spannung gedimmt, sondern mit dem Versorgungsstrom. Da dieser Stromfluss aber im Normalfall mit einem Vorwiderstand eingestellt wird, ist durch das Ohmsche Gesetz dieser Stromfluss bei konstantem Widerstand wieder direkt proportional zur Höhe der Versorgungsspannung.&lt;br /&gt;
&lt;br /&gt;
Im wesentlichen geht es also immer um diese Kennlinie, trägt man die Versorgungsspannung entlang der Zeitachse auf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
[[Bild:PWM_1.gif]]&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Fläche unter der Kurve ist dabei ein direktes Maß für die Energie die dem System zugeführt wird. Bei geringerer Energie ist die Helligkeit geringer, bei höherer Energie entsprechend heller.&lt;br /&gt;
&lt;br /&gt;
Jedoch gibt es noch einen zweiten Weg, die dem System zugeführte Energie zu verringern. Anstatt die Spannung abzusenken, ist es auch möglich die volle Versorgungsspannung über einen geringeren Zeitraum anzulegen. Man muß nur dafür Sorge tragen, dass im Endeffekt die einzelnen Pulse nicht mehr wahrnehmbar sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
[[Bild:PWM_Theorie_1.gif]]&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Fläche unter den Rechtecken hat in diesem Fall dieselbe Größe wie die Fläche unter der Spannung V=, glättet man die Spannung also mit einem Kondensator, ergibt sich eine niedrigere konstante Spannung. Die Rechtecke sind zwar höher, aber dafür schmäler. Die Flächen sind aber dieselben. Diese Lösung hat den Vorteil, dass keine Spannung geregelt werden muss, sondern der Verbraucher immer mit derselben Spannung versorgt wird.&lt;br /&gt;
&lt;br /&gt;
Und genau das ist das Prinzip einer PWM. Durch die Abgabe von Pulsen wird die abgegebene Energiemenge gesteuert. Es ist auf einem µC wesentlich einfacher Pulse mit einem definiertem Puls/Pausen Verhältnis zu erzeugen als eine Spannung zu variieren.&lt;br /&gt;
&lt;br /&gt;
==PWM und der Timer==&lt;br /&gt;
Der Timer1 des Mega8 unterstützt direkt das Erzeugen von PWM. Beginnt der Timer beispielsweise bei 0 zu zählen, so schaltet er gleichzeitig einen Ausgangspin ein. Erreicht der Zähler einen bestimmten Wert X, so schaltet er den Ausgangspin wieder aus und zählt weiter bis zu seiner Obergrenze. Danach wiederholt sich das Spielchen. Der Timer beginnt wieder bei 0 und schaltet gleichzeitig den Ausgangspin ein etc. Durch Verändern von X kann man daher steuern, wie lange der Ausgangspin im Verhältnis zur kompletten Zeit, die der Timer benötigt, um seine Obergrenze zu erreichen, eingeschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Dabei gibt es aber verwirrenderweise verschiedene Arten der PWM:&lt;br /&gt;
* Fast PWM&lt;br /&gt;
* Phasen-korrekte PWM&lt;br /&gt;
* Phasen- und frequenzkorrekte PWM&lt;br /&gt;
Für die Details zu jedem PWM-Modus sei auf das Datenblatt verwiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Fast PWM===&lt;br /&gt;
&lt;br /&gt;
Die Fast PWM gibt es beim Mega8 mit mehreren unterschiedlichen Bit-Zahlen. Bei den Bit-Zahlen geht es immer darum, wie weit der Timer zählt, bevor ein Rücksetzen des Timers auf 0 erfolgt&lt;br /&gt;
&lt;br /&gt;
* Modus 5: 8 Bit Fast PWM - Der Timer zählt bis 255&lt;br /&gt;
* Modus 6: 9 Bit Fast PWM - Der Timer zählt bis 511&lt;br /&gt;
* Modus 7: 10 Bit Fast PWM - Der Timer zählt bis 1023&lt;br /&gt;
* Modus 14: Fast PWM mit beliebiger Schrittzahl (festgelegt durch &#039;&#039;&#039;ICR1&#039;&#039;&#039;)&lt;br /&gt;
* Modus 15: Fast PWM mit beliebiger Schrittzahl (festgelegt durch &#039;&#039;&#039;OCR1A&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich funktioniert der Fast-PWM Modus so, dass der Timer bei 0 anfängt zu zählen, wobei natürlich der eingestellte Vorteiler des Timers berücksichtigt wird. Erreicht der Timer einen bestimmten Zählerstand (festgelegt durch die Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039; und &#039;&#039;&#039;OCR1B&#039;&#039;&#039;) wird eine Aktion ausgelöst. Je nach Festlegung kann der entsprechende µC Pin (OC1A und OC1B) entweder &lt;br /&gt;
* umgeschaltet&lt;br /&gt;
* auf 1 gesetzt&lt;br /&gt;
* auf 0 gesetzt&lt;br /&gt;
werden. Wird der OC1A/OC1B Pin so konfiguriert, dass er auf 1 oder 0 gesetzt wird, so wird automatisch der entsprechende Pin beim Timerstand 0 auf den jeweils gegenteiligen Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
Der OC1A Pin befindet sich beim Mega8 am Port B, konkret am Pin &#039;&#039;&#039;PB1&#039;&#039;&#039;. Dieser Pin muss über das zugehörige Datenrichtungsregister &#039;&#039;&#039;DDRB&#039;&#039;&#039; auf Ausgang gestellt werden. Anders als beim UART geschieht dies nicht automatisch.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel zeigt den Modus 14. Dabei wird der Timer-Endstand durch das Register &#039;&#039;&#039;ICR1&#039;&#039;&#039; festgelegt. Des Weiteren wird die Funktion des OC1A Pins so festgelegt, dass der Pin bei einem Timer Wert von 0 auf 1 gesetzt wird und bei Erreichen des im &#039;&#039;&#039;OCR1A&#039;&#039;&#039; Registers festgelegten Wertes auf 0 gesetzt wird. Der Vorteiler des Timers, bzw. der ICR-Wert wird zunächst so eingestellt, dass eine an &#039;&#039;&#039;PB1&#039;&#039;&#039; angeschlossene LED noch blinkt, die Auswirkungen unterschiedlicher Register Werte gut beobachtet werden können. Den Vorteiler zu verringern ist kein Problem, hier geht es aber darum, zu demonstrieren wie PWM funktioniert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;FF0000&amp;quot;&amp;gt;Hinweis:&amp;lt;/font&amp;gt; Wie überall im ATMega8 ist darauf zu achten, dass beim Beschreiben eines 16-Bit Registers zuerst das High-Byte und dann das Low-Byte geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp1         = r17&lt;br /&gt;
 &lt;br /&gt;
.equ XTAL = 4000000&lt;br /&gt;
 &lt;br /&gt;
    rjmp    init&lt;br /&gt;
&lt;br /&gt;
;.include &amp;quot;keys.asm&amp;quot; &lt;br /&gt;
;&lt;br /&gt;
;&lt;br /&gt;
 &lt;br /&gt;
init:&lt;br /&gt;
    ldi      temp1, HIGH(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out      SPH, temp1&lt;br /&gt;
    ldi      temp1, LOW(RAMEND)&lt;br /&gt;
    out      SPL, temp1&lt;br /&gt;
  &lt;br /&gt;
    ;&lt;br /&gt;
    ; Timer 1 einstellen&lt;br /&gt;
    ;&lt;br /&gt;
    ; Modus 14:&lt;br /&gt;
    ;    Fast PWM, Top von ICR1&lt;br /&gt;
    ;&lt;br /&gt;
    ;     WGM13    WGM12   WGM11    WGM10&lt;br /&gt;
    ;      1        1       1        0&lt;br /&gt;
    ;&lt;br /&gt;
    ;    Timer Vorteiler: 256&lt;br /&gt;
    ;     CS12     CS11    CS10&lt;br /&gt;
    ;      1        0       0&lt;br /&gt;
    ;&lt;br /&gt;
    ; Steuerung des Ausgangsport: Set at BOTTOM, Clear at match&lt;br /&gt;
    ;     COM1A1   COM1A0&lt;br /&gt;
    ;      1        0&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
    ldi      temp1, 1&amp;lt;&amp;lt;COM1A1 | 1&amp;lt;&amp;lt;WGM11&lt;br /&gt;
    out      TCCR1A, temp1&lt;br /&gt;
&lt;br /&gt;
    ldi      temp1, 1&amp;lt;&amp;lt;WGM13 | 1&amp;lt;&amp;lt;WGM12 | 1&amp;lt;&amp;lt;CS12&lt;br /&gt;
    out      TCCR1B, temp1&lt;br /&gt;
&lt;br /&gt;
    ;&lt;br /&gt;
    ; den Endwert (TOP) für den Zähler setzen&lt;br /&gt;
    ; der Zähler zählt bis zu diesem Wert&lt;br /&gt;
    ;&lt;br /&gt;
    ldi      temp1, 0x6F&lt;br /&gt;
    out      ICR1H, temp1&lt;br /&gt;
    ldi      temp1, 0xFF&lt;br /&gt;
    out      ICR1L, temp1&lt;br /&gt;
&lt;br /&gt;
    ;&lt;br /&gt;
    ; der Compare Wert&lt;br /&gt;
    ; Wenn der Zähler diesen Wert erreicht, wird mit&lt;br /&gt;
    ; obiger Konfiguration der OC1A Ausgang abgeschaltet&lt;br /&gt;
    ; Sobald der Zähler wieder bei 0 startet, wird der&lt;br /&gt;
    ; Ausgang wieder auf 1 gesetzt&lt;br /&gt;
    ;&lt;br /&gt;
    ldi      temp1, 0x3F&lt;br /&gt;
    out      OCR1AH, temp1&lt;br /&gt;
    ldi      temp1, 0xFF&lt;br /&gt;
    out      OCR1AL, temp1&lt;br /&gt;
 &lt;br /&gt;
    ; Den Pin OC1A zu guter letzt noch auf Ausgang schalten&lt;br /&gt;
    ldi      temp1, 0x02&lt;br /&gt;
    out      DDRB, temp1&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
    rjmp     main&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird dieses Programm laufen gelassen, dann ergibt sich eine blinkende LED. Die LED ist die Hälfte der Blinkzeit an und in der anderen Hälfte des Blinkzyklus aus. Wird der Compare Wert in &#039;&#039;&#039;OCR1A&#039;&#039;&#039; verändert, so lässt sich das Verhältnis von LED Einzeit zu Auszeit verändern. Ist die LED wie im I/O Kapitel angeschlossen, so führen höhere &#039;&#039;&#039;OCR1A&#039;&#039;&#039; Werte dazu, dass die LED nur kurz aufblitzt und in der restlichen Zeit dunkel bleibt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi      temp1, 0x6D&lt;br /&gt;
    out      OCR1AH, temp1&lt;br /&gt;
    ldi      temp1, 0xFF&lt;br /&gt;
    out      OCR1AL, temp1&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sinngemäß führen kleinere &#039;&#039;&#039;OCR1A&#039;&#039;&#039; Werte dazu, daß die LED länger leuchtet und die Dunkelphasen kürzer werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi      temp1, 0x10&lt;br /&gt;
    out      OCR1AH, temp1&lt;br /&gt;
    ldi      temp1, 0xFF&lt;br /&gt;
    out      OCR1AL, temp1&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem die Funktion und das Zusammenspiel der einzelnen Register jetzt klar ist, ist es Zeit aus dem Blinken ein echtes Dimmen zu machen. Dazu genügt es den Vorteiler des Timers auf 1 zu setzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi      temp1, 1&amp;lt;&amp;lt;WGM13 | 1&amp;lt;&amp;lt;WGM12 | 1&amp;lt;&amp;lt;CS10&lt;br /&gt;
    out      TCCR1B, temp1&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Werden wieder die beiden &#039;&#039;&#039;OCR1A&#039;&#039;&#039; Werte 0x6DFF und 0x10FF ausprobiert, so ist deutlich zu sehen, dass die LED scheinbar unterschiedlich hell leuchtet. Dies ist allerdings eine optische Täuschung. Die LED blinkt nach wie vor, nur blinkt sie so schnell, daß dies für uns nicht mehr wahrnehmbar ist. Durch Variation der Einschalt- zu Ausschaltzeit kann die LED auf viele verschiedene Helligkeitswerte eingestellt werden.&lt;br /&gt;
&lt;br /&gt;
Theoretisch wäre es möglich die LED auf 0x6FFF verschiedene Helligkeitswerte einzustellen. Dies deshalb, weil in &#039;&#039;&#039;ICR1&#039;&#039;&#039; genau dieser Wert als Endwert für den Timer festgelegt worden ist. Dieser Wert könnte genauso gut kleiner oder größer eingestellt werden. Um eine LED zu dimmen ist der Maximalwert aber hoffnungslos zu hoch. Für diese Aufgabe reicht eine Abstufung von 256 oder 512 Stufen normalerweise völlig aus. Genau für diese Fälle gibt es die anderen Modi. Anstatt den Timer Endstand mittels &#039;&#039;&#039;ICR1&#039;&#039;&#039; festzulegen, genügt es den Timer einfach nur in den 8, 9 oder 10 Bit Modus zu konfigurieren und damit eine PWM mit 256 (8 Bit), 512 (9 Bit) oder 1024 (10 Bit) Stufen zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
===PWM mit Timer 2 (OCR2)===&lt;br /&gt;
&lt;br /&gt;
===Phasen-korrekte PWM===&lt;br /&gt;
&lt;br /&gt;
===Phasen- und Frequenz-korrekte PWM===&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==PWM in Software==&lt;br /&gt;
Die Realisierung einer PWM mit einem Timer, wobei der Timer die ganze Arbeit macht, ist zwar einfach, hat aber einen Nachteil. Für jede einzelne PWM ist ein eigener Timer notwendig (Ausnahme: Der Timer 1 besitzt 2 Compare Register und kann damit 2 PWM Stufen erzeugen). Und davon gibt es in einem Mega8 nicht all zu viele.&lt;br /&gt;
&lt;br /&gt;
Es geht auch anders: Es ist durchaus möglich viele PWM Stufen mit nur einem Timer zu realisieren. Der Timer wird nur noch dazu benötigt, eine stabile und konstante Zeitbasis zu erhalten. Von dieser Zeitbasis wird alles weitere abgeleitet.&lt;br /&gt;
&lt;br /&gt;
===Prinzip===&lt;br /&gt;
Das Grundprinzip ist dabei sehr einfach: Eine PWM ist ja im Grunde nichts anderes als eine Blinkschleife, bei der das Verhältnis von Ein- zu Auszeit variabel eingestellt werden kann. Die Blinkfrequenz selbst ist konstant und ist so schnell, dass das eigentliche Blinken nicht mehr wahrgenommen werden kann. Das lässt sich aber auch alles in einer ISR realisieren:&lt;br /&gt;
* Ein Timer (Timer0) wird so aufgesetzt, dass er eine Overflow-Interruptfunktion (ISR) mit dem 256-fachen der gewünschten Blinkfrequenz aufruft.&lt;br /&gt;
* In der ISR wird ein weiterer Zähler betrieben (&amp;lt;i&amp;gt;PWMCounter&amp;lt;/i&amp;gt;), der ständig von 0 bis 255 zählt.&lt;br /&gt;
* Für jede zu realisierende PWM Stufe gibt es einen Grenzwert. Liegt der Wert des PWMCounters unter diesem Wert, so wird der entsprechende Port Pin eingeschaltet. Liegt er darüber, so wird der entsprechende Port Pin ausgeschaltet&lt;br /&gt;
&lt;br /&gt;
Damit wird im Grunde nichts anderes gemacht, als die Funktionalität der Fast-PWM in Software nachzubilden. Da man dabei aber nicht auf ein einziges OCR Register angewiesen ist, sondern in gewissen Umfang beliebig viele davon implementieren kann, kann man auch beliebig viele PWM Stufen erzeugen.&lt;br /&gt;
&lt;br /&gt;
===Programm===&lt;br /&gt;
Am &#039;&#039;&#039;Port B&#039;&#039;&#039; werden an den Pins &#039;&#039;&#039;PB0&#039;&#039;&#039; bis &#039;&#039;&#039;PB5&#039;&#039;&#039; insgesamt 6 LEDs gemäß der Verschaltung aus dem [[AVR-Tutorial: IO-Grundlagen|I/O Artikel]] angeschlossen. Jede einzelne LED kann durch Setzen eines Wertes von 0 bis 127 in die zugehörigen Register &amp;lt;i&amp;gt;ocr_1&amp;lt;/i&amp;gt; bis &amp;lt;i&amp;gt;ocr_6&amp;lt;/i&amp;gt; auf einen anderen Helligkeitswert eingestellt werden. Die PWM-Frequenz (Blinkfrequenz) jeder LED beträgt: ( 4000000 / 256 ) / 127 = 123Hz. Dies reicht aus, um das Blinken unter die Wahrnehmungsschwelle zu drücken und die LEDs gleichmässig erleuchtet erscheinen zu lassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp  = r16&lt;br /&gt;
&lt;br /&gt;
.def PWMCount = r17&lt;br /&gt;
&lt;br /&gt;
.def ocr_1 = r18                      ; Helligkeitswert Led1: 0 .. 127&lt;br /&gt;
.def ocr_2 = r19                      ; Helligkeitswert Led2: 0 .. 127&lt;br /&gt;
.def ocr_3 = r20                      ; Helligkeitswert Led3: 0 .. 127&lt;br /&gt;
.def ocr_4 = r21                      ; Helligkeitswert Led4: 0 .. 127&lt;br /&gt;
.def ocr_5 = r22                      ; Helligkeitswert Led5: 0 .. 127&lt;br /&gt;
.def ocr_6 = r23                      ; Helligkeitswert Led6: 0 .. 127&lt;br /&gt;
 &lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
 &lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp&lt;br /&gt;
        ldi     temp, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp&lt;br /&gt;
	&lt;br /&gt;
        ldi     temp, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     ocr_1, 0&lt;br /&gt;
        ldi     ocr_2, 1&lt;br /&gt;
        ldi     ocr_3, 10&lt;br /&gt;
        ldi     ocr_4, 20&lt;br /&gt;
        ldi     ocr_5, 80&lt;br /&gt;
        ldi     ocr_6, 127&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, 1&amp;lt;&amp;lt;CS00         ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, 1&amp;lt;&amp;lt;TOIE0        ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
 &lt;br /&gt;
        sei&lt;br /&gt;
 &lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
 &lt;br /&gt;
timer0_overflow:                      ; Timer 0 Overflow Handler&lt;br /&gt;
        inc     PWMCount              ; den PWM Zähler von 0 bis&lt;br /&gt;
        cpi     PWMCount, 128         ; 127 zählen lassen&lt;br /&gt;
        brne    WorkPWM&lt;br /&gt;
        clr     PWMCount&lt;br /&gt;
&lt;br /&gt;
WorkPWM:&lt;br /&gt;
        ldi     temp, 0b11000000      ; 0 .. Led an, 1 .. Led aus&lt;br /&gt;
&lt;br /&gt;
        cp      PWMCount, ocr_1       ; Ist der Grenzwert für Led 1 erreicht&lt;br /&gt;
        brlo    OneOn&lt;br /&gt;
        ori     temp, $01&lt;br /&gt;
&lt;br /&gt;
OneOn:  cp      PWMCount, ocr_2       ; Ist der Grenzwert für Led 2 erreicht&lt;br /&gt;
        brlo    TwoOn&lt;br /&gt;
        ori     temp, $02&lt;br /&gt;
&lt;br /&gt;
TwoOn:  cp      PWMCount, ocr_3       ; Ist der Grenzwert für Led 3 erreicht&lt;br /&gt;
        brlo    ThreeOn&lt;br /&gt;
        ori     temp, $04&lt;br /&gt;
&lt;br /&gt;
ThreeOn:cp      PWMCount, ocr_4       ; Ist der Grenzwert für Led 4 erreicht&lt;br /&gt;
        brlo    FourOn&lt;br /&gt;
        ori     temp, $08&lt;br /&gt;
&lt;br /&gt;
FourOn: cp      PWMCount, ocr_5       ; Ist der Grenzwert für Led 5 erreicht&lt;br /&gt;
        brlo    FiveOn&lt;br /&gt;
        ori     temp, $10&lt;br /&gt;
&lt;br /&gt;
FiveOn: cp      PWMCount, ocr_6       ; Ist der Grenzwert für Led 6 erreicht&lt;br /&gt;
        brlo    SetBits&lt;br /&gt;
        ori     temp, $20&lt;br /&gt;
&lt;br /&gt;
SetBits:                              ; Die neue Bitbelegung am Port ausgeben&lt;br /&gt;
        out     PORTB, temp&lt;br /&gt;
&lt;br /&gt;
        reti&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde man die LEDs anstatt direkt an einen Port anzuschliessen, über ein oder mehrere [[AVR-Tutorial: Schieberegister|Schieberegister]] anschließen, so kann auf diese Art eine relativ große Anzahl an LEDs gedimmt werden. Natürlich müsste man die softwareseitige LED Ansteuerung gegenüber der hier gezeigten verändern, aber das PWM Prinzip könnte so übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[PWM]]&lt;br /&gt;
* [[AVR-GCC-Tutorial#PWM (Pulsweitenmodulation)|AVR-GCC-Tutorial: PWM]]&lt;br /&gt;
* [[Soft-PWM]] - optimierte Software-PWM in C&lt;br /&gt;
* [[LED-Fading]] - LED dimmen mit PWM&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Tasten|&lt;br /&gt;
zurücklink=AVR-Tutorial: Tasten|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Schieberegister|&lt;br /&gt;
vorlink=AVR-Tutorial: Schieberegister}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|PWM]]&lt;/div&gt;</summary>
		<author><name>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Power_Management&amp;diff=62974</id>
		<title>AVR-Tutorial: Power Management</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Power_Management&amp;diff=62974"/>
		<updated>2012-01-02T20:21:56Z</updated>

		<summary type="html">&lt;p&gt;Mkleemann: /* Quellen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;ACHTUNG!&#039;&#039;&#039;&lt;br /&gt;
Dieser Artikel befindet sich noch im Aufbau!&lt;br /&gt;
&lt;br /&gt;
Vorallem in batteriebetriebenen Systemen spielt die Leistungsaufnahme eine wichtige Rolle, d.h. sie soll so niedrig wie möglich gehalten werden um eine lange Laufzeit zu erreichen. Den sparsamen Umgang mit der verfügbaren el. Ladung nennt man &#039;&#039;&#039;Power Management&#039;&#039;&#039; (dt. Energiesparen).&lt;br /&gt;
&lt;br /&gt;
Im Rahmen des Power Managements stehen uns beispielsweise die Sleep-Modi zur Verfügung, mit denen wir bestimmte Module zeitweise deaktivieren können. Andere garnicht genutzte Module können wir durch entsprechende Konfiguration (z.B. in den Fuses) auch komplett deaktivieren.&lt;br /&gt;
&lt;br /&gt;
== Theorie ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Sleep Modi ===&lt;br /&gt;
&lt;br /&gt;
Welche Sleep-Modi es gibt, hängt vom verwendeten µC ab, dieser Artikel nimmt jedoch Bezug auf den ATmega32. Um einen der verfügbaren Sleep-Modi des ATmega32 zu betreten müssen folgende Schritte ausgeführt werden&lt;br /&gt;
&lt;br /&gt;
# Das SE-Bit im MCUCR-Register wird auf 1 gesetzt&lt;br /&gt;
# Die SMx-Bits im MCUCR-Register je nach gewünschtem Modus setzen&lt;br /&gt;
# Der SLEEP-Befehl wird ausgeführt&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller geht dann sofort in den SLEEP-Modus, d.h. noch vor eventuell anstehenden Interrupts, und wacht erst wieder auf wenn ein Signal eines geeigneten Moduls (je nach Modus) ihn aufweckt.&lt;br /&gt;
&lt;br /&gt;
Die Arbeit wird dann mit der ersten Anweisung hinter dem SLEEP-Befehl wieder aufgenommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ MCUCR - MCU Control Register&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
! Bit&lt;br /&gt;
| style=&amp;quot;width:12%&amp;quot; | 7&lt;br /&gt;
| style=&amp;quot;width:12%&amp;quot; | 6&lt;br /&gt;
| style=&amp;quot;width:12%&amp;quot; | 5&lt;br /&gt;
| style=&amp;quot;width:12%&amp;quot; | 4&lt;br /&gt;
| style=&amp;quot;width:12%; background-color:#c0c0c0&amp;quot; | 3&lt;br /&gt;
| style=&amp;quot;width:12%; background-color:#c0c0c0&amp;quot; | 2&lt;br /&gt;
| style=&amp;quot;width:12%; background-color:#c0c0c0&amp;quot; | 1&lt;br /&gt;
| style=&amp;quot;width:12%; background-color:#c0c0c0&amp;quot; | 0&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
! Bezeichnung&lt;br /&gt;
| SE&lt;br /&gt;
| SM2&lt;br /&gt;
| SM1&lt;br /&gt;
| SM0&lt;br /&gt;
| style=&amp;quot;background-color:#c0c0c0&amp;quot; | ISC11&lt;br /&gt;
| style=&amp;quot;background-color:#c0c0c0&amp;quot; | ISC10&lt;br /&gt;
| style=&amp;quot;background-color:#c0c0c0&amp;quot; | ISC01&lt;br /&gt;
| style=&amp;quot;background-color:#c0c0c0&amp;quot; | ISC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
;Bit 7 - SE: Sleep Enable&lt;br /&gt;
:Mit diesem Bit wird bestimmt ob der Sleep-Befehl ausgeführt wird (1) oder nicht (0).&lt;br /&gt;
&lt;br /&gt;
;Bit 6..4 - SM2..0: Sleep Mode Select&lt;br /&gt;
:Mit diesen drei Bits wird der gewünschte Sleep-Modus gewählt&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! SM2&lt;br /&gt;
! SM1&lt;br /&gt;
! SM0&lt;br /&gt;
! style=&amp;quot;text-align:left&amp;quot; | Sleep Modus&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Idle&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | ADC Noise Reduction&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Power-down&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Power-save&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Reserved&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Reserved&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Standby&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Extended Standby&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt; Nur verfügbar mit externem Taktgeber&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Modi Übersicht====&lt;br /&gt;
&lt;br /&gt;
Generell ist der Modus zu wählen, der die meisten nicht benötigten Module abschaltet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!&lt;br /&gt;
! colspan=&amp;quot;5&amp;quot; | Aktive Takte &lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Aktive Oszillatoren&lt;br /&gt;
! colspan=&amp;quot;6&amp;quot; | Weckquellen&lt;br /&gt;
|- style=&amp;quot;background-color:#f0f0f0&amp;quot;&lt;br /&gt;
| Sleep Modus&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;CPU&amp;lt;/sub&amp;gt;&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;FLASH&amp;lt;/sub&amp;gt;&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;IO&amp;lt;/sub&amp;gt;&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;ADC&amp;lt;/sub&amp;gt;&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;ASY&amp;lt;/sub&amp;gt;&lt;br /&gt;
| Haupttaktgeber&lt;br /&gt;
| Timer Oszillator&lt;br /&gt;
| INT2&amp;lt;br /&amp;gt;INT1&amp;lt;br /&amp;gt;INT0&lt;br /&gt;
| TWI Address Match&lt;br /&gt;
| Timer2&lt;br /&gt;
| SPM/EEPROM Ready&lt;br /&gt;
| ADC&lt;br /&gt;
| Andere I/O&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Idle&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | ADC Noise Reduction&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| &lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Power-down&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Power-save&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Standby&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| x&lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Extended Standby&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt; Nur verfügbar bei externer Taktquelle&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt; Wenn AS2-Bit in ASSR-Register gesetzt&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt; Nur INT2 oder Level Interrupt INT1 und INT0&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Manuelles Deaktivieren ===&lt;br /&gt;
Einzelne Module können auch manuell deaktviert werden um Strom zu sparen, das bietet sich vorallem an wenn bestimmte Module im gegebenen Projekt generell nicht benötigt werden und damit deaktiviert werden können.&lt;br /&gt;
&lt;br /&gt;
==== Analog to Digital Converter ====&lt;br /&gt;
todo...&lt;br /&gt;
&lt;br /&gt;
==== Analog Comparator ====&lt;br /&gt;
Der Analogkomparator ist standardmäßig aktiviert. Um ihn zu deaktivieren, muss man ADC (Bit 7) im Register ACSR setzen.&lt;br /&gt;
&lt;br /&gt;
==== Brown-Out Detector ====&lt;br /&gt;
Der Brown-Out Detector lässt sich entweder durch das BODEN-Bit in den Fuses oder mit entsprechenden Befehlen aktivieren oder deaktivieren. Das Fuse-Bit ist standardmäßig gesetzt (Achtung: Umgekehrte Logik!) und der BOD damit deaktiviert.&lt;br /&gt;
&lt;br /&gt;
==== Watchdog ====&lt;br /&gt;
Auch der Watchdog-Timer lässt sich in den Fuses standardmäßig aktivieren/deaktivieren, hier über das WDTON-Bit.&lt;br /&gt;
Natürlich geht auch das softwareseitig [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Watchdog]&lt;br /&gt;
&lt;br /&gt;
== Praxis ==&lt;br /&gt;
&#039;&#039;&#039;TODO:&#039;&#039;&#039; ASM-Quellcode Beispiele&lt;br /&gt;
&lt;br /&gt;
== Quellen ==&lt;br /&gt;
[http://www.atmel.com/dyn/resources/prod_documents/doc2503.pdf ATMEL AVR ATmega32 Datenblatt]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
{{Navigation_zurückhoch|&lt;br /&gt;
zurücktext=Power Management|&lt;br /&gt;
zurücklink=AVR-Tutorial: Power Management|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie: AVR-Tutorial|Power Management]]&lt;/div&gt;</summary>
		<author><name>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_SRAM&amp;diff=62973</id>
		<title>AVR-Tutorial: SRAM</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_SRAM&amp;diff=62973"/>
		<updated>2012-01-02T20:21:32Z</updated>

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

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

		<summary type="html">&lt;p&gt;Mkleemann: /* Ansteuerung mehrerer Servos mittels Timer */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Die Seite ist noch im entstehen und bis sie einigermassen vollständig ist, noch kein Teil des Tutorials&lt;br /&gt;
&lt;br /&gt;
= Allgemeines über Servos =&lt;br /&gt;
&lt;br /&gt;
= Stromversorung =&lt;br /&gt;
Werden Servos an einem µC betrieben, so ist es am Besten, sie aus einer eigenen Stromquelle (Akku) zu betreiben. Manche Servos erzeugen kleine Störungen auf der Versorungsspannung, die einen µC durchaus zum Abstürzen bringen können. Muss man Servos gemeinsam mit einem µC von derselben Stromquelle betreiben, so sollte man sich gleich darauf einrichten, diesen Störimpulsen mit Kondensatoren zu Leibe rücken zu müssen. Unter Umständen ist hier auch eine Mischung aus kleinen, schnellen Kondensatoren (100nF) und etwas größeren, aber dafür auch langsameren Kondensatoren (einige µF) notwendig.&lt;br /&gt;
&lt;br /&gt;
Die eindeutig beste Option ist es aber, die Servos strommäßig vom µC zu entkoppeln und ihnen ihre eigene Stromquelle zu geben. Servos sind nicht besonders heikel. Auch im Modellbau müssen sie mit unterschiedlichen Spannungen zurechtkommen, bedingt durch die dort übliche Versorung aus Akkus, die im Laufe der Betriebszeit des Modells natürlich durch die Entladung ihre Voltzahl immer weiter reduzieren. Im Modellbau werden Akkus mit 4 oder 5 Zellen verwendet, sodass Servos mit Spannungen von ca. 4V bis hinauf zu ca. 6V zurecht kommen müssen, wobei randvolle Akkus diese 6V schon auch mal überschreiten können. Bei sinkenden Spannungslage verlieren Servos naturgemäß etwas an Kraft bzw. werden in ihrer Stellgeschwindigkeit unter Umständen langsamer.&lt;br /&gt;
&lt;br /&gt;
Die Servos werden dann nur mit ihrer Masseleitung und natürlich mit ihrer Impulsleitung mit dem µC verbunden.&lt;br /&gt;
&lt;br /&gt;
= Das Servo-Impulstelegram =&lt;br /&gt;
Das Signal, das an den Servo geschickt wird, hat eine Länge von ungefähr 20ms. Diese 20ms sind nicht besonders kritisch und sind ein Überbleibsel von der Technik mit der mehrere Kanäle über die Funkstrecke einer Fernsteuerung übertragen werden. Für das Servo wichtig ist die Impulsdauer in der ersten Phase eines Servosignals. Nominell ist dieser Impuls zwischen 1ms und 2ms lang. Wobei das jeweils die Endstellungen des Servos sind, an denen es noch nicht mechanisch begrenzt wird. Eine Pulslänge von 1.5ms wäre dann Servomittelstellung. Für die Positionsauswertung des Servos haben die 20ms Wiederholdauer keine besondere Bedeutung, sieht man einmal davon ab, dass ein Servo bei kürzeren Zeiten entsprechend öfter Positionsimpulse bekommt und daher auch öfter die Position gegebenenfalls korrigiert, was möglicherweise in einem etwas höheren Stromverbrauch resultiert.&lt;br /&gt;
&lt;br /&gt;
Umgekehrt lässt sich definitiv Strom sparen, indem die Pulse ganz ausgesetzt werden: Der Servo bleibt in der Position, in der er sich gerade befindet - korrigiert sich aber auch nicht mehr. Kommen die Impulse selten, also z.B. alle 50ms, läuft der Servo langsamer in seine Zielposition (praktische Erfahrungen, vermutlich nirgends spezifiziert). Dieses Verhalten lässt sich nutzen, um die manchmal unerwünschten ruckartigen Bewegungen eines Servos abzumildern.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Servo.gif|framed|center| Servo Impulsdiagramm]]&lt;br /&gt;
&lt;br /&gt;
Den meisten Servos macht es nichts aus, wenn die Länge des Servoprotokolls anstelle von 20ms auf zb 10ms verkürzt wird. Bei der Generierung des Servosignals muss man daher den 20ms keine besondere Beachtung schenken. Eine kleine Pause nach dem eigentlichen Positionssignal reicht in den meisten Fällen aus und es spielt keine allzugroße Rolle, wie lange diese Pause tatsächlich ist. Generiert man das Imulsdiagramm zb. mit einem Timer, so orientiert man sich daher daran, dass man den 1.0 - 2.0ms Puls gut generieren kann und nicht an den 20ms.&lt;br /&gt;
&lt;br /&gt;
Reale Servos haben allerdings in den Endstellungen noch Reserven, so dass man bei vielen Servos auch Pulslängen von 0.9 bis 2.1 oder sogar noch kleinere/größere Werte benutzen kann. Allerdings sollte man hier etwas Vorsicht walten lassen. Wenn das Servo unbelastet in einer der Endstellungen deutlich zu &#039;knurren&#039; anfängt, dann hat man es übertrieben. Das Servo ist an seinen mechanischen Endanschlag gefahren worden und auf Dauer wird das der Motor bzw. das Getriebe nicht aushalten.&lt;br /&gt;
&lt;br /&gt;
= Programmierung =&lt;br /&gt;
== einfache Servoansteuerung mittels Warteschleifen ==&lt;br /&gt;
&lt;br /&gt;
Im folgenden Programm wurden einfache Warteschleifen auf die im Tutorial übliche Taktfrequen von 4Mhz angepasst, so dass sich die typischen Servo-Pulsdauern ergeben. Ein am Port D, beliebiger Pin angeschlossenes Servo dreht damit ständig vor und zurück. Die Servoposition kann durch laden eines Wertes im Bereich 1 bis ca 160 in das Register r18 und anschliessendem Aufruf von servoPuls in einen Puls für ein Servo umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.equ XTAL = 4000000&lt;br /&gt;
 &lt;br /&gt;
         rjmp    init&lt;br /&gt;
&lt;br /&gt;
init:&lt;br /&gt;
          ldi      r16, HIGH(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
          out      SPH, r16&lt;br /&gt;
          ldi      r16, LOW(RAMEND)&lt;br /&gt;
          out      SPL, r16&lt;br /&gt;
&lt;br /&gt;
          ldi r16, 0xFF&lt;br /&gt;
          out DDRD, r16&lt;br /&gt;
&lt;br /&gt;
loop:     ldi  r18, 0&lt;br /&gt;
           &lt;br /&gt;
loop1:    inc r18&lt;br /&gt;
          cpi r18, 160&lt;br /&gt;
          breq loop2&lt;br /&gt;
&lt;br /&gt;
          rcall servoPuls&lt;br /&gt;
&lt;br /&gt;
          rjmp loop1&lt;br /&gt;
&lt;br /&gt;
loop2:    dec r18&lt;br /&gt;
          cpi r18, 0&lt;br /&gt;
          breq loop1&lt;br /&gt;
&lt;br /&gt;
          rcall servoPuls&lt;br /&gt;
&lt;br /&gt;
          rjmp loop2&lt;br /&gt;
&lt;br /&gt;
servoPuls:&lt;br /&gt;
          push r18&lt;br /&gt;
          ldi r16, 0xFF         ; Ausgabepin auf 1&lt;br /&gt;
          out PORTD, r16&lt;br /&gt;
          &lt;br /&gt;
          rcall wait_puls        ; die Wartezeit abwarten&lt;br /&gt;
&lt;br /&gt;
          ldi r16, 0x00         ; Ausgabepin wieder auf 0&lt;br /&gt;
          out PORTD, r16&lt;br /&gt;
&lt;br /&gt;
          rcall wait_pause       ; und die Pause hinten nach abwarten&lt;br /&gt;
          pop r18&lt;br /&gt;
          ret&lt;br /&gt;
;&lt;br /&gt;
wait_pause:&lt;br /&gt;
          ldi r19, 15&lt;br /&gt;
w_paus_1: rcall wait_1ms&lt;br /&gt;
          dec r19&lt;br /&gt;
          brne w_paus_1&lt;br /&gt;
          ret&lt;br /&gt;
;&lt;br /&gt;
wait_1ms: ldi r18, 10           ; 1 Millisekunde warten&lt;br /&gt;
w_loop2:  ldi r17, 132          ; Es muessen bei 4 Mhz 4000 Zyklen verbraten werden&lt;br /&gt;
w_loop1:  dec r17               ; die innerste Schleife umfasst 3 Takte und wird 132&lt;br /&gt;
          brne w_loop1          ; mal abgearbeitet: 132 * 3 = 396 Takte&lt;br /&gt;
          dec r18               ; dazu noch 4 Takte für die äussere Schleife = 400&lt;br /&gt;
          brne w_loop2          ; 10 Wiederholungen: 4000 Takte&lt;br /&gt;
          ret                   ; der ret ist nicht eingerechnet&lt;br /&gt;
;&lt;br /&gt;
; r18 muss mit der Anzahl der Widerholungen belegt werden&lt;br /&gt;
; vernünftige Werte laufen von 1 bis ca 160&lt;br /&gt;
wait_puls:&lt;br /&gt;
w_loop4:  ldi r17, 10           ; die variable Zeit abwarten&lt;br /&gt;
w_loop3:  dec r17&lt;br /&gt;
          brne w_loop3&lt;br /&gt;
          dec r18&lt;br /&gt;
          brne w_loop4&lt;br /&gt;
&lt;br /&gt;
          rcall wait_1ms         ; und noch 1 Millisekunde drauflegen&lt;br /&gt;
          ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie meistens gilt auch hier: Warteschleifen sind in der Programmierung nicht erwünscht. Der Prozessor kann in diesen Warteschleifen nichts anderes machen. Etwas ausgeklügeltere Programme, bei denen mehrere Dinge gleichzeitig gemacht werden sollen, sind damit nicht vernünftig realisierbar.&lt;br /&gt;
Daher sollte die Methode mittels Warteschleifen nur dann benutzt werden, wenn dies nicht benötigt wird, wie zb einem simplen Servotester, bei dem man die Servoposition zb durch Auslesen eines Potis mit dem ADC festlegt.&lt;br /&gt;
&lt;br /&gt;
Ausserdem ist die Berechnung der Warteschleifen auf eine bestimmte Taktfrequenz unangenehm und fehleranfällig :-)&lt;br /&gt;
&lt;br /&gt;
== einfache Servoansteuerung mittels Timer ==&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung mit dem 16-Bit Timer 1 ===&lt;br /&gt;
Im Prinzip programmiert man sich hier eine Software-PWM. Beginnt er Timer bei 0 zu zählen (Overflow Interrupt oder Compare Match Interrupt im CTC Modus), so setzt man den gewünschten Ausgangspin auf 1. Ein Compare Match Register wird so mit einem berechneten Wert versorgt, daß es nach der gewünschten Pulszeit einen Interrupt auslöst. In der zugehörigen Interrupt Routine wird der Pin dann wieder auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Auch hier wieder: Der Vorteiler des Timers so wird so eingestellt, dass man die Pulszeit gut mit dem Compare Match erreichen kann, die nachfolgende Pause, bis der Timer dann seinen Overflow hat (oder den CTC Clear macht) ist von untergeordneter Bedeutung. Man nimmt was vom Zählbereich des Timers übrig bleibt.&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung mit dem 8-Bit Timer 2 ===&lt;br /&gt;
&lt;br /&gt;
Mit einem 8 Bit Timer ist es gar nicht so einfach, sowohl die Zeiten für den Servopuls als auch die für die Pause danach unter einen Hut zu bringen. Abhilfe schafft ein Trick.&lt;br /&gt;
&lt;br /&gt;
Der Timer wird so eingestellt, dass sich der Servopuls gut erzeugen lässt. Dazu wird der Timer in den CTC Modus gestellt und das zugehörige Vergleichsregister so eingestellt, dass sich die entsprechenden Interrupts zeitlich so ergeben, wie es für einen Puls benötigt wird. In einem Aufruf des Interrupts wird der Ausgangspin für das Servo auf 1 gestellt, im nächsten wird er wieder auf 0 gestellt. Die kleine Pause bis zum nächsten Servoimpuls wird so erzeugt, dass eine gewisse Anzahl an Interrupt Aufrufen einfach nichts gemacht wird. Ähnlich wie bei einer PWM wird also auch hier wieder ein Zähler installiert, der die Anzahl der Interrupt Aufrufe mitzählt und immer wieder auf 0 zurückgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Servoposition steht im Register OCR2. Rein rechnerisch beträgt ihr Wertebereich:&lt;br /&gt;
&lt;br /&gt;
1ms      4000000 / 64 / 1000     OCR2 =  62.5&lt;br /&gt;
&lt;br /&gt;
2ms      4000000 / 64 /  500     OCR2 = 125&lt;br /&gt;
&lt;br /&gt;
mit einer Mittelstellung von ( 62.5 + 125 ) / 2 = 93.75&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m16def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.equ XTAL = 4000000&lt;br /&gt;
 &lt;br /&gt;
          rjmp    init&lt;br /&gt;
&lt;br /&gt;
.org OC2addr&lt;br /&gt;
          rjmp    Compare_vect&lt;br /&gt;
&lt;br /&gt;
init: &lt;br /&gt;
          ldi      r16, HIGH(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
          out      SPH, r16&lt;br /&gt;
          ldi      r16, LOW(RAMEND)&lt;br /&gt;
          out      SPL, r16&lt;br /&gt;
&lt;br /&gt;
          ldi  r16, 0x80&lt;br /&gt;
          out  DDRB, r16                ; Servo Ausgangspin -&amp;gt; Output&lt;br /&gt;
&lt;br /&gt;
          ldi  r17, 0                   ; Software-Zähler&lt;br /&gt;
            &lt;br /&gt;
          ldi  r16, 120&lt;br /&gt;
          out  OCR2, r16                ; OCR0 ist der Servowert&lt;br /&gt;
&lt;br /&gt;
          ldi  r16, 1&amp;lt;&amp;lt;OCIE2&lt;br /&gt;
          out  TIMSK, r16&lt;br /&gt;
&lt;br /&gt;
          ldi  r16, (1&amp;lt;&amp;lt;WGM21) | (1&amp;lt;&amp;lt;CS22) ; CTC, Prescaler: 64&lt;br /&gt;
          out  TCCR2, r16&lt;br /&gt;
&lt;br /&gt;
          sei&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
          rjmp main&lt;br /&gt;
&lt;br /&gt;
Compare_vect:&lt;br /&gt;
          in   r18, SREG&lt;br /&gt;
          inc  r17&lt;br /&gt;
          cpi  r17, 1&lt;br /&gt;
          breq PulsOn&lt;br /&gt;
          cpi  r17, 2&lt;br /&gt;
          breq PulsOff&lt;br /&gt;
          cpi  r17, 10&lt;br /&gt;
          brne return&lt;br /&gt;
          ldi  r17, 0&lt;br /&gt;
return:   out  SREG, r18&lt;br /&gt;
          reti&lt;br /&gt;
&lt;br /&gt;
PulsOn:   sbi  PORTB, 0&lt;br /&gt;
          rjmp return&lt;br /&gt;
&lt;br /&gt;
PulsOff:  cbi  PORTB, 0&lt;br /&gt;
          rjmp return&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung mehrerer Servos mittels Timer ==&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=7-Segment-Anzeige|&lt;br /&gt;
zurücklink=AVR-Tutorial: 7-Segment-Anzeige|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Watchdog|&lt;br /&gt;
vorlink=AVR-Tutorial: Watchdog}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Servo]]&lt;br /&gt;
[[Category:Servos]]&lt;/div&gt;</summary>
		<author><name>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Speicher&amp;diff=62970</id>
		<title>AVR-Tutorial: Speicher</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Speicher&amp;diff=62970"/>
		<updated>2012-01-02T20:20:21Z</updated>

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

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

		<summary type="html">&lt;p&gt;Mkleemann: /* Weblinks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Bisher beschränkten sich die meisten Programme auf reine Ausgabe an einem Port. Möchte man Eingaben machen, so ist der Anschluss von [[AVR-Tutorial: IO-Grundlagen|Tasten]] an einen Port unumgänglich. Dabei erheben sich aber 2 Probleme&lt;br /&gt;
* Wie kann man erreichen, dass ein Tastendruck nur einmal ausgewertet wird?&lt;br /&gt;
* Tasten müssen entprellt werden&lt;br /&gt;
&lt;br /&gt;
==Erkennung von Flanken am Tasteneingang==&lt;br /&gt;
Möchte man eine Taste auswerten, bei der eine Aktion nicht ausgeführt werden soll, &amp;lt;i&amp;gt;solange&amp;lt;/i&amp;gt; die Taste gedrückt ist, sondern nur einmal &amp;lt;i&amp;gt;beim Drücken&amp;lt;/i&amp;gt; einer Taste, dann ist eine Erkennung der Schaltflanke der Weg zum Ziel. Anstatt eine gedrückte Taste zu erkennen, wird bei einer Flankenerkennung der Wechsel des Zustands des Eingangspins detektiert. Dazu vergleicht man in regelmäßigen Zeitabständen den momentanen Zustand des Eingangs mit dem Zustand zum vorhergehenden Zeitpunkt. Unterscheiden sich die beiden, so hat man eine Schaltflanke erkannt und kann darauf reagieren. Solange sich der Tastenzustand nicht ändert, egal ob die Taste gedrückt oder losgelassen ist, unternimmt man nichts.&lt;br /&gt;
&lt;br /&gt;
Die Erkennung des Zustandswechsels kann am einfachsten mit einer [[AVR-Tutorial:_Logik#XOR|XOR]] (Exklusiv Oder) Verknüpfung durchgeführt werden. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle|min-width:20em;text-align:center;}}&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Wahrheitstabelle&amp;amp;nbsp;XOR&#039;&#039;&#039;&lt;br /&gt;
!width=&amp;quot;30%&amp;quot;| A ||width=&amp;quot;30%&amp;quot;| B ||width=&amp;quot;30%&amp;quot;| Ergebnis&lt;br /&gt;
|- &lt;br /&gt;
|  0 ||  0 ||  0&lt;br /&gt;
|- &lt;br /&gt;
|  0 ||  1 ||  1&lt;br /&gt;
|- &lt;br /&gt;
|  1 ||  0 ||  1&lt;br /&gt;
|- &lt;br /&gt;
|  1 ||  1 ||  0&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nur dann, wenn sich der Zustand A vom Zustand B unterscheidet, taucht im Ergebnis eine 1 auf. Sind A und B gleich, so ist das Ergebnis 0.&lt;br /&gt;
&lt;br /&gt;
A ist bei uns der vorhergehende Zustand eines Tasters, B ist der jetzige Zustand so wie er vom Port Pin eingelesen wurde. Verknüpft man die beiden mit einem [[AVR-Tutorial:_Logik#XOR|XOR]], so bleiben im Ergebnis genau an jenen Bitpositionen 1en übrig, an denen sich der jetzige Zustand vom vorhergehenden unterscheidet.&lt;br /&gt;
&lt;br /&gt;
Nun ist bei Tastern aber nicht nur der erkannte Flankenwechsel interessant, sondern auch in welchen Zustand die Taste gewechselt hat:&lt;br /&gt;
* Ist dieser 0, so wurde die Taste gedrückt.&lt;br /&gt;
* Ist dieser 1, so wurde die Taste losgelassen.&lt;br /&gt;
&lt;br /&gt;
Eine einfache [[AVR-Tutorial:_Logik#UND|UND]] Verknüpfung der Tastenflags mit dem [[AVR-Tutorial:_Logik#XOR|XOR]] Ergebnis liefert diese Information&lt;br /&gt;
&lt;br /&gt;
Das folgende Programm soll bei jedem Tastendruck eines Tasters am Port D (egal welcher  Pin) eine LED am Port B0 in den jeweils anderen Zustand umschalten:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def key_old   = r3&lt;br /&gt;
.def key_now   = r4&lt;br /&gt;
&lt;br /&gt;
.def temp1     = r17&lt;br /&gt;
.def temp2     = r18&lt;br /&gt;
&lt;br /&gt;
.equ key_pin   = PIND&lt;br /&gt;
.equ key_port  = PORTD&lt;br /&gt;
.equ key_ddr   = DDRD&lt;br /&gt;
 &lt;br /&gt;
.equ led_port  = PORTB&lt;br /&gt;
.equ led_ddr   = DDRB&lt;br /&gt;
.equ LED       = 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      ldi  temp1, 1&amp;lt;&amp;lt;LED&lt;br /&gt;
      out  led_ddr, temp1         ; den LED Port auf Ausgang&lt;br /&gt;
&lt;br /&gt;
      ldi  temp1, $00             ; den Key Port auf Eingang schalten&lt;br /&gt;
      out  key_ddr, temp1&lt;br /&gt;
      ldi  temp1, $FF             ; die Pullup Widerstände aktivieren&lt;br /&gt;
      out  key_port, temp1&lt;br /&gt;
&lt;br /&gt;
      mov  key_old, temp1         ; bisher war kein Taster gedrückt&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
      in   key_now, key_pin       ; den jetzigen Zustand der Taster holen&lt;br /&gt;
      mov  temp1, key_now         ; und in temp1 sichern&lt;br /&gt;
      eor  key_now, key_old       ; mit dem vorhergehenden Zustand XOR&lt;br /&gt;
      mov  key_old, temp1         ; und den jetzigen Zustand für den nächsten&lt;br /&gt;
                                  ; Schleifendurchlauf als alten Zustand merken&lt;br /&gt;
&lt;br /&gt;
      breq loop                   ; Das Ergebnis des XOR auswerten:&lt;br /&gt;
                                  ; wenn keine Taste gedrückt war -&amp;gt; neuer Schleifendurchlauf&lt;br /&gt;
&lt;br /&gt;
      and  temp1, key_now         ; War das ein 1-&amp;gt;0 Übergang, wurde der Taster also&lt;br /&gt;
                                  ; gedrückt (in key_now steht das Ergebnis vom XOR)&lt;br /&gt;
      brne loop                   ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      in   temp1, led_port        ; den Zustand der LED umdrehen&lt;br /&gt;
      com  temp1&lt;br /&gt;
      out  led_port, temp1&lt;br /&gt;
&lt;br /&gt;
      rjmp  loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Probiert man diese Implementierung aus, so stellt man fest: Sie funktioniert nicht besonders gut. Es kann vorkommen, dass bei einem Tastendruck die LED zwar kurzzeitig umschaltet aber gleich darauf wieder ausgeht. Genauso gut kann es passieren, dass die LED beim Loslassen einer Taste ebenfalls wieder den Zustand wechselt. Die Ursache dafür ist: Taster prellen.&lt;br /&gt;
&lt;br /&gt;
==Prellen==&lt;br /&gt;
Das Prellen entsteht in der Mechanik der Tasten: Eine Kontaktfeder wird durch das Drücken des Tastelements auf einen anderen Kontakt gedrückt. Wenn die Kontaktfeder das Kontaktfeld berührt, federt sie jedoch nach. Dies kann soweit gehen, dass die Feder wieder vom Feld abhebt und den elektrischen Kontakt kurzzeitig wieder unterbricht. Auch wenn diese Effekte sehr kurz sind, sind sie für einen Mikrocontroller viel zu lang. Für ihn sieht die Situation so aus, dass beim Drücken der Taste eine Folge von: &amp;lt;i&amp;gt;Taste geschlossen&amp;lt;/i&amp;gt;, &amp;lt;i&amp;gt;Taste offen&amp;lt;/i&amp;gt;, &amp;lt;i&amp;gt;Taste geschlossen&amp;lt;/i&amp;gt;, &amp;lt;i&amp;gt;Taste offen&amp;lt;/i&amp;gt; Ereignissen am Port sichtbar sind, die sich dann nach einiger Zeit auf den Zustand &amp;lt;i&amp;gt;Taste geschlossen&amp;lt;/i&amp;gt; einpendelt. Beim Loslassen der Taste dann dasselbe Spielchen in der umgekehrten Richtung.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Entprellen.png|framed|center| Signal eines prellenden Tasters]]&lt;br /&gt;
&lt;br /&gt;
Nun kann es natürlich sein, dass ein neuer Taster zunächst überhaupt nicht prellt. Ist der Taster vom Hersteller nicht explizit als &#039;prellfreier Taster&#039; verkauft worden, besteht aber kein Grund zur Freude. Auch wenn der Taster heute noch nicht prellt, irgendwann wird er es tun. Dann nämlich, wenn die Kontaktfeder ein wenig ihrer Spannung verliert und ausleiert, bzw. wenn sich die Kontaktflächen durch häufige Benutzung abgewetzt haben.&lt;br /&gt;
&lt;br /&gt;
==Entprellung==&lt;br /&gt;
Aus diesem Grund müssen Tasten entprellt werden. Im Prinzip kann eine Entprellung sehr einfach durchgeführt werden. Ein &#039;Tastendruck&#039; wird nicht bei der Erkennung der ersten Flanke akzeptiert, sondern es wird noch eine zeitlang gewartet. Ist nach Ablauf dieser Zeitdauer die Taste immer noch gedrückt, dann wird diese Flanke als Tastendruck akzeptiert und ausgewertet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def key_old   = r3&lt;br /&gt;
.def key_now   = r4&lt;br /&gt;
&lt;br /&gt;
.def temp1     = r17&lt;br /&gt;
.def temp2     = r18&lt;br /&gt;
&lt;br /&gt;
.equ key_pin   = PIND&lt;br /&gt;
.equ key_port  = PORTD&lt;br /&gt;
.equ key_ddr   = DDRD&lt;br /&gt;
 &lt;br /&gt;
.equ led_port  = PORTB&lt;br /&gt;
.equ led_ddr   = DDRB&lt;br /&gt;
.equ LED       = 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      ldi  temp1, 1&amp;lt;&amp;lt;LED&lt;br /&gt;
      out  led_ddr, temp1         ; den Led Port auf Ausgang&lt;br /&gt;
&lt;br /&gt;
      ldi  temp1, $00             ; den Key Port auf Eingang schalten&lt;br /&gt;
      out  key_ddr, temp1&lt;br /&gt;
      ldi  temp1, $FF             ; die Pullup Widerstände aktivieren&lt;br /&gt;
      out  key_port, temp1&lt;br /&gt;
&lt;br /&gt;
      mov  key_old, temp1         ; bisher war kein Taster gedrückt&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
      in   key_now, key_pin       ; den jetzigen Zustand der Taster holen&lt;br /&gt;
      mov  temp1, key_now         ; und in temp1 sichern&lt;br /&gt;
      eor  key_now, key_old       ; mit dem vorhergehenden Zustand XOR&lt;br /&gt;
      mov  key_old, temp1         ; und den jetzigen Zustand für den nächsten&lt;br /&gt;
                                  ; Schleifendurchlauf als alten Zustand merken&lt;br /&gt;
&lt;br /&gt;
      breq loop                   ; Das Ergebnis des XOR auswerten:&lt;br /&gt;
                                  ; wenn keine Taste gedrückt war -&amp;gt; neuer Schleifendurchlauf&lt;br /&gt;
&lt;br /&gt;
      and  temp1, key_now         ; War das ein 1-&amp;gt;0 Übergang, wurde der Taster also&lt;br /&gt;
                                  ; gedrückt (in key_now steht das Ergebnis vom XOR)&lt;br /&gt;
      brne loop                   ;&lt;br /&gt;
&lt;br /&gt;
      ldi  temp1, $FF             ; ein bisschen warten ...&lt;br /&gt;
wait1:&lt;br /&gt;
      ldi  temp2, $FF&lt;br /&gt;
wait2:&lt;br /&gt;
      dec  temp2&lt;br /&gt;
      brne wait2&lt;br /&gt;
      dec  temp1&lt;br /&gt;
      brne wait1&lt;br /&gt;
                                  ; ... und nachsehen, ob die Taste immer noch gedrückt ist&lt;br /&gt;
      in   temp1, key_pin&lt;br /&gt;
      and  temp1, key_now&lt;br /&gt;
      brne loop&lt;br /&gt;
&lt;br /&gt;
      in   temp1, led_port        ; den Zustand der LED umdrehen&lt;br /&gt;
      com  temp1&lt;br /&gt;
      out  led_port, temp1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      rjmp  loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie lange gewartet werden muss, hängt im wesentlichen von der mechanischen Qualität und dem Zustand des Tasters ab. Neue und qualitativ hochwertige Taster prellen wenig, ältere Taster prellen mehr. Grundsätzlich prellen aber alle mechanischen Taster irgendwann. Man sollte nicht dem Trugschluss verfallen, daß ein Taster nur weil er heute nicht erkennbar prellt, dieses auch in einem halben Jahr nicht tut.&lt;br /&gt;
&lt;br /&gt;
==Kombinierte Entprellung und Flankenerkennung==&lt;br /&gt;
Von Herrn Peter Dannegger stammt eine [[Entprellung|clevere Routine]], die mit wenig Aufwand an einem Port gleichzeitig bis zu 8 Tasten erkennen und zuverlässig entprellen kann. Dazu wird ein Timer benutzt, der mittels Overflow-Interrupt einen Basistakt erzeugt. Die Zeitdauer von einem Interrupt zum nächsten ist dabei ziemlich unkritisch. Sie sollte sich im Bereich von 5 bis 50 Millisekunden bewegen.&lt;br /&gt;
&lt;br /&gt;
In jedem Overflow Interrupt wird der jetzt am Port anliegende Tastenzustand mit dem Zustand im letzten Timer Interrupt verglichen. Nur dann wenn an einem Pin eine Änderung festgestellt werden kann (Flankenerkennung) wird dieser Tastendruck zunächst registriert. Ein clever aufgebauter Zähler zählt danach die Anzahl der Timer Overflows mit, die die Taste nach Erkennung der Flanke im gedrückten Zustand verharrte. Wurde die Taste nach Erkennung der Flanke 4 mal hintereinander als gedrückt identifiziert, so wird der Tastendruck weitergemeldet.&lt;br /&gt;
&lt;br /&gt;
===Einfache Tastenentprellung und Abfrage===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def iwr0      = r1&lt;br /&gt;
.def iwr1      = r2&lt;br /&gt;
 &lt;br /&gt;
.def key_old   = r3&lt;br /&gt;
.def key_state = r4&lt;br /&gt;
.def key_press = r5&lt;br /&gt;
&lt;br /&gt;
.def temp1     = r17&lt;br /&gt;
&lt;br /&gt;
.equ key_pin   = PIND&lt;br /&gt;
.equ key_port  = PORTD&lt;br /&gt;
.equ key_ddr   = DDRD&lt;br /&gt;
&lt;br /&gt;
.def leds      = r16&lt;br /&gt;
.equ led_port  = PORTB&lt;br /&gt;
.equ led_ddr   = DDRB&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
    rjmp    init&lt;br /&gt;
&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
    rjmp    timer_overflow0&lt;br /&gt;
&lt;br /&gt;
timer_overflow0:               ; Timer Overflow Interrupt&lt;br /&gt;
&lt;br /&gt;
    push    r0                 ; temporäre Register sichern&lt;br /&gt;
    in      r0, SREG&lt;br /&gt;
    push    r0&lt;br /&gt;
    push    iwr0&lt;br /&gt;
    push    iwr1&lt;br /&gt;
&lt;br /&gt;
get8key:                       ;/old      state     iwr1      iwr0&lt;br /&gt;
    mov     iwr0, key_old      ;00110011  10101010            00110011&lt;br /&gt;
    in      key_old, key_pin   ;11110000&lt;br /&gt;
    eor     iwr0, key_old      ;                              11000011&lt;br /&gt;
    com     key_old            ;00001111&lt;br /&gt;
    mov     iwr1, key_state    ;                    10101010&lt;br /&gt;
    or      key_state, iwr0    ;          11101011&lt;br /&gt;
    and     iwr0, key_old      ;                              00000011&lt;br /&gt;
    eor     key_state, iwr0    ;          11101000&lt;br /&gt;
    and     iwr1, iwr0         ;                    00000010&lt;br /&gt;
    or      key_press, iwr1    ; gedrückte Taste merken&lt;br /&gt;
;&lt;br /&gt;
;&lt;br /&gt;
    pop     iwr1               ; Register wiederherstellen&lt;br /&gt;
    pop     iwr0&lt;br /&gt;
    pop     r0&lt;br /&gt;
    out     SREG, r0&lt;br /&gt;
    pop     r0&lt;br /&gt;
    reti&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
init:&lt;br /&gt;
    ldi      temp1, HIGH(RAMEND)&lt;br /&gt;
    out      SPH, temp1&lt;br /&gt;
    ldi      temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out      SPL, temp1&lt;br /&gt;
&lt;br /&gt;
    ldi      temp1, 0xFF&lt;br /&gt;
    out      led_ddr, temp1&lt;br /&gt;
&lt;br /&gt;
    ldi      temp1, 0xFF            ; Tasten sind auf Eingang&lt;br /&gt;
    out      key_port, temp1        ; Pullup Widerstände ein&lt;br /&gt;
&lt;br /&gt;
    ldi      temp1, 1&amp;lt;&amp;lt;CS02 | 1&amp;lt;&amp;lt;CS00   ; Timer mit Vorteiler 1024&lt;br /&gt;
    out      TCCR0, temp1&lt;br /&gt;
    ldi      temp1, 1&amp;lt;&amp;lt;TOIE0            ; Timer Overflow Interrupt einrichten&lt;br /&gt;
    out      TIMSK, temp1&lt;br /&gt;
 &lt;br /&gt;
    clr      key_old                ; die Register für die Tastenauswertung im&lt;br /&gt;
    clr      key_state              ; Timer Interrupt initialisieren&lt;br /&gt;
    clr      key_press&lt;br /&gt;
&lt;br /&gt;
    sei                             ; und los gehts: Timer frei&lt;br /&gt;
&lt;br /&gt;
    ldi      leds, 0xFF&lt;br /&gt;
    out      led_port, leds&lt;br /&gt;
main:&lt;br /&gt;
    cli                             ; &lt;br /&gt;
    mov      temp1, key_press       ; Einen ev. Tastendruck merken und ...&lt;br /&gt;
    clr      key_press              ; Tastendruck zurücksetzen&lt;br /&gt;
    sei&lt;br /&gt;
&lt;br /&gt;
    cpi      temp1, 0               ; Tastendruck auswerten. Wenn eine von 8 Tasten&lt;br /&gt;
    breq     main                   ; gedrückt worden wäre, wäre ein entsprechendes&lt;br /&gt;
                                    ; Bit in key_press gesetzt gewesen&lt;br /&gt;
&lt;br /&gt;
    eor      leds, temp1            ; Die zur Taste gehörende Led umschalten&lt;br /&gt;
    out      led_port, leds&lt;br /&gt;
    rjmp     main&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Tastenentprellung, Abfrage und Autorepeat===&lt;br /&gt;
Gerade bei Zahlenreihen ist oft eine &#039;&#039;&#039;Autorepeat&#039;&#039;&#039; Funktion eine nützliche Einrichtung: Drückt der Benutzer eine Taste wird eine Funktion ausgelöst. Drückt er eine Taste und hält sie gedrückt, so setzt nach kurzer Zeit der &#039;&#039;&#039;Autorepeat&#039;&#039;&#039; ein. Das System verhält sich so, als ob die Taste in schneller Folge immer wieder gedrückt und wieder losgelassen würde.&lt;br /&gt;
&lt;br /&gt;
Leider muss hier für die Wartezeit ein Register im oberen Bereich benutzt werden. Der &#039;&#039;&#039;ldi&#039;&#039;&#039; Befehl macht dies notwendig. Alternativ könnte man die Wartezeiten beim Init in eines der unteren Register laden und von dort das &amp;lt;i&amp;gt;Repeat Timer&amp;lt;/i&amp;gt; Register &#039;&#039;&#039;key_rep&#039;&#039;&#039; jeweils nachladen.&lt;br /&gt;
&lt;br /&gt;
Alternativ wurde in diesem Code auch die Rolle des Registers &#039;&#039;&#039;key_state&#039;&#039;&#039; umgedreht. Ein gesetztes 1 Bit bedeutet hier, dass die zugehörige Taste zur Zeit gedrückt ist.&lt;br /&gt;
&lt;br /&gt;
Insgesamt ist dieser Code eine direkte Umsetzung des von Herrn Dannegger vorgestellten C-Codes. Durch die Möglichkeit eines Autorepeats bei gedrückter Taste erhöhen sich die Möglichkeiten im Aufbau von Benutzereingaben enorm. Das bischen Mehraufwand im Vergleich zum vorher vorgestellten Code, rechtfertigt dies auf jeden Fall.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def iwr0          = r1&lt;br /&gt;
.def iwr1          = r2&lt;br /&gt;
 &lt;br /&gt;
.def key_state     = r4&lt;br /&gt;
.def key_press     = r5&lt;br /&gt;
.def key_rep_press = r6&lt;br /&gt;
.def key_rep       = r16&lt;br /&gt;
&lt;br /&gt;
.def temp1         = r17&lt;br /&gt;
&lt;br /&gt;
.equ KEY_PIN       = PIND&lt;br /&gt;
.equ KEY_PORT      = PORTD&lt;br /&gt;
.equ KEY_DDR       = DDRD&lt;br /&gt;
&lt;br /&gt;
.equ KEY_REPEAT_START = 50&lt;br /&gt;
.equ KEY_REPEAT_NEXT  = 15&lt;br /&gt;
&lt;br /&gt;
.def leds      = r20&lt;br /&gt;
.equ led_port  = PORTB&lt;br /&gt;
.equ led_ddr   = DDRB&lt;br /&gt;
&lt;br /&gt;
.equ XTAL  = 4000000&lt;br /&gt;
&lt;br /&gt;
    rjmp    init&lt;br /&gt;
&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
    rjmp    timer_overflow0&lt;br /&gt;
&lt;br /&gt;
timer_overflow0:               ; Timer Overflow Interrupt&lt;br /&gt;
&lt;br /&gt;
    push    r0                 ; temporäre Register sichern&lt;br /&gt;
    in      r0, SREG&lt;br /&gt;
    push    r0&lt;br /&gt;
&lt;br /&gt;
    push    r16&lt;br /&gt;
    ; TCNT0 so vorladen, dass der nächste Overflow nach 10 ms auftritt.&lt;br /&gt;
    ldi     r16, -( XTAL / 1024 * 10 / 1000)&lt;br /&gt;
    ;                ^      ^     ^^^^^^^^^&lt;br /&gt;
    ;                |      |      = 10 ms&lt;br /&gt;
    ;                |      Vorteiler&lt;br /&gt;
    ;                Quarz-Takt&lt;br /&gt;
    ;&lt;br /&gt;
    out     TCNT0, r16&lt;br /&gt;
    pop     r16&lt;br /&gt;
&lt;br /&gt;
get8key:&lt;br /&gt;
    in      r0, KEY_PIN        ; Tasten einlesen&lt;br /&gt;
    com     r0                 ; gedrückte Taste werden zu 1&lt;br /&gt;
    eor     r0, key_state      ; nur Änderunden berücksichtigen&lt;br /&gt;
    and     iwr0, r0           ; in iwr0 und iwr1 zählen&lt;br /&gt;
    com     iwr0&lt;br /&gt;
    and     iwr1, r0&lt;br /&gt;
    eor     iwr1, iwr0&lt;br /&gt;
    and     r0, iwr0&lt;br /&gt;
    and     r0, iwr1&lt;br /&gt;
    eor     key_state, r0      ;&lt;br /&gt;
    and     r0, key_state&lt;br /&gt;
    or      key_press, r0      ; gedrückte Taste merken&lt;br /&gt;
    tst     key_state          ; irgendeine Taste gedrückt ?&lt;br /&gt;
    breq    get8key_rep        ; Nein, Zeitdauer zurücksetzen&lt;br /&gt;
    dec     key_rep&lt;br /&gt;
    brne    get8key_finish;    ; Zeit abgelaufen?&lt;br /&gt;
    mov     key_rep_press, key_state&lt;br /&gt;
    ldi     key_rep, KEY_REPEAT_NEXT&lt;br /&gt;
    rjmp    get8key_finish&lt;br /&gt;
&lt;br /&gt;
get8key_rep:&lt;br /&gt;
    ldi     key_rep, KEY_REPEAT_START&lt;br /&gt;
&lt;br /&gt;
get8key_finish:&lt;br /&gt;
    pop     r0                 ; Register wiederherstellen&lt;br /&gt;
    out     SREG, r0&lt;br /&gt;
    pop     r0&lt;br /&gt;
    reti&lt;br /&gt;
;&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
init:&lt;br /&gt;
    ldi      temp1, HIGH(RAMEND)&lt;br /&gt;
    out      SPH, temp1&lt;br /&gt;
    ldi      temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out      SPL, temp1&lt;br /&gt;
&lt;br /&gt;
    ldi      temp1, 0xFF&lt;br /&gt;
    out      led_ddr, temp1&lt;br /&gt;
&lt;br /&gt;
    ldi      temp1, 0xFF            ; Tasten sind auf Eingang&lt;br /&gt;
    out      KEY_PORT, temp1        ; Pullup Widerstände ein&lt;br /&gt;
&lt;br /&gt;
    ldi      temp1, 1&amp;lt;&amp;lt;CS00 | 1&amp;lt;&amp;lt;CS02&lt;br /&gt;
    out      TCCR0, temp1&lt;br /&gt;
    ldi      temp1, 1&amp;lt;&amp;lt;TOIE0		; Timer mit Vorteiler 1024&lt;br /&gt;
    out      TIMSK, temp1&lt;br /&gt;
 &lt;br /&gt;
    clr      key_state&lt;br /&gt;
    clr      key_press&lt;br /&gt;
    clr      key_rep_press&lt;br /&gt;
    clr      key_rep&lt;br /&gt;
&lt;br /&gt;
    ldi      leds, 0xFF&lt;br /&gt;
    out      led_port, leds&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
                                    ; einen einzelnen Tastendruck auswerten&lt;br /&gt;
    cli&lt;br /&gt;
    mov      temp1, key_press&lt;br /&gt;
    clr      key_press&lt;br /&gt;
    sei&lt;br /&gt;
&lt;br /&gt;
    cpi      temp1, 0x01            ; Nur dann wenn Taste 0 gedrückt wurde&lt;br /&gt;
    breq     toggle&lt;br /&gt;
&lt;br /&gt;
                                    ; Tasten Autorepeat auswerten&lt;br /&gt;
    cli&lt;br /&gt;
    mov      temp1, key_rep_press&lt;br /&gt;
    clr      key_rep_press&lt;br /&gt;
    sei&lt;br /&gt;
                                    ; Nur dann wenn Taste 0 gehalten wurde&lt;br /&gt;
    cpi      temp1, 0x01&lt;br /&gt;
    breq     toggle&lt;br /&gt;
&lt;br /&gt;
    rjmp     main                   ; Hauptschleife abgeschlossen&lt;br /&gt;
&lt;br /&gt;
toggle:&lt;br /&gt;
    eor      leds, temp1            ; Die zur Taste gehörende Led umschalten&lt;br /&gt;
    out      led_port, leds&lt;br /&gt;
    rjmp     main&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Fallbeispiel==&lt;br /&gt;
Das folgende Programm hat durchaus praktischen Wert. Es zeigt auf dem LCD den ASCII Code dezimal und in hexadezimal an, sowie das zugehörige LCD-Zeichen. An den &#039;&#039;&#039;PORTD&#039;&#039;&#039; werden an den Pins 0 und 1 jeweils 1 Taster angeschlossen. Mit dem einen Taster kann der ASCII Code erhöht werden, mit dem anderen Taster wird der ASCII Code erniedrigt. Auf beiden Tastern liegt jeweils ein Autorepeat, sodass jeder beliebige Code einfach angesteuert werden kann. Insbesondere die ASCII Codes größer als 128 sind interessant :-)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def iwr0          = r1&lt;br /&gt;
.def iwr1          = r2&lt;br /&gt;
 &lt;br /&gt;
.def key_state     = r4&lt;br /&gt;
.def key_press     = r5&lt;br /&gt;
.def key_rep_press = r6&lt;br /&gt;
.def key_rep       = r16&lt;br /&gt;
&lt;br /&gt;
.def temp1         = r17&lt;br /&gt;
&lt;br /&gt;
.equ KEY_PIN       = PIND&lt;br /&gt;
.equ KEY_PORT      = PORTD&lt;br /&gt;
.equ KEY_DDR       = DDRD&lt;br /&gt;
&lt;br /&gt;
.equ KEY_REPEAT_START = 40&lt;br /&gt;
.equ KEY_REPEAT_NEXT  = 15&lt;br /&gt;
&lt;br /&gt;
.def code          = r20&lt;br /&gt;
&lt;br /&gt;
.equ XTAL = 4000000&lt;br /&gt;
&lt;br /&gt;
    rjmp    init&lt;br /&gt;
&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
    rjmp    timer_overflow0&lt;br /&gt;
&lt;br /&gt;
timer_overflow0:               ; Timer Overflow Interrupt&lt;br /&gt;
&lt;br /&gt;
    push    r0                 ; temporäre Register sichern&lt;br /&gt;
    in      r0, SREG&lt;br /&gt;
    push    r0&lt;br /&gt;
&lt;br /&gt;
    push    r16&lt;br /&gt;
    ldi     r16, -( XTAL / 1024 * 10 / 1000 + 1 )&lt;br /&gt;
    out     TCNT0, r16&lt;br /&gt;
    pop     r16&lt;br /&gt;
&lt;br /&gt;
get8key:&lt;br /&gt;
    in      r0, KEY_PIN        ; Tasten einlesen&lt;br /&gt;
    com     r0                 ; gedrückte Taste werden zu 1&lt;br /&gt;
    eor     r0, key_state      ; nur Änderunden berücksichtigen&lt;br /&gt;
    and     iwr0, r0           ; in iwr0 und iwr1 zählen&lt;br /&gt;
    com     iwr0&lt;br /&gt;
    and     iwr1, r0&lt;br /&gt;
    eor     iwr1, iwr0&lt;br /&gt;
    and     r0, iwr0&lt;br /&gt;
    and     r0, iwr1&lt;br /&gt;
    eor     key_state, r0      ;&lt;br /&gt;
    and     r0, key_state&lt;br /&gt;
    or      key_press, r0      ; gedrückte Taste merken&lt;br /&gt;
    tst     key_state          ; irgendeine Taste gedrückt ?&lt;br /&gt;
    breq    get8key_rep        ; Nein, Zeitdauer zurücksetzen&lt;br /&gt;
    dec     key_rep&lt;br /&gt;
    brne    get8key_finish;    ; Zeit abgelaufen?&lt;br /&gt;
    mov     key_rep_press, key_state&lt;br /&gt;
    ldi     key_rep, KEY_REPEAT_NEXT&lt;br /&gt;
    rjmp    get8key_finish&lt;br /&gt;
&lt;br /&gt;
get8key_rep:&lt;br /&gt;
    ldi     key_rep, KEY_REPEAT_START&lt;br /&gt;
&lt;br /&gt;
get8key_finish:&lt;br /&gt;
    pop     r0                 ; Register wiederherstellen&lt;br /&gt;
    out     SREG, r0&lt;br /&gt;
    pop     r0&lt;br /&gt;
    reti&lt;br /&gt;
;&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
init:&lt;br /&gt;
    ldi      temp1, HIGH(RAMEND)&lt;br /&gt;
    out      SPH, temp1&lt;br /&gt;
    ldi      temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out      SPL, temp1&lt;br /&gt;
&lt;br /&gt;
    ldi      temp1, 0xFF            ; Tasten sind auf Eingang&lt;br /&gt;
    out      KEY_PORT, temp1        ; Pullup Widerstände ein&lt;br /&gt;
&lt;br /&gt;
    rcall     lcd_init&lt;br /&gt;
    rcall     lcd_clear&lt;br /&gt;
&lt;br /&gt;
    ldi      temp1, 1&amp;lt;&amp;lt;CS00 | 1&amp;lt;&amp;lt;CS02&lt;br /&gt;
    out      TCCR0, temp1&lt;br /&gt;
    ldi      temp1, 1&amp;lt;&amp;lt;TOIE0		; Timer mit Vorteiler 1024&lt;br /&gt;
    out      TIMSK, temp1&lt;br /&gt;
 &lt;br /&gt;
    clr      key_state&lt;br /&gt;
    clr      key_press&lt;br /&gt;
    clr      key_rep_press&lt;br /&gt;
    clr      key_rep&lt;br /&gt;
&lt;br /&gt;
    ldi      code, 0x30&lt;br /&gt;
    rjmp     update&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
    cli                            ; normaler Tastendruck&lt;br /&gt;
    mov      temp1, key_press&lt;br /&gt;
    clr      key_press&lt;br /&gt;
    sei&lt;br /&gt;
    cpi      temp1, 0x01           ; Increment&lt;br /&gt;
    breq     increment&lt;br /&gt;
    cpi      temp1, 0x02           ; Decrement&lt;br /&gt;
    breq     decrement&lt;br /&gt;
&lt;br /&gt;
    cli                            ; gedrückt und halten -&amp;gt; repeat&lt;br /&gt;
    mov      temp1, key_rep_press&lt;br /&gt;
    clr      key_rep_press&lt;br /&gt;
    sei&lt;br /&gt;
    cpi      temp1, 0x01           ; Increment&lt;br /&gt;
    breq     increment&lt;br /&gt;
    cpi      temp1, 0x02           ; Decrement&lt;br /&gt;
    breq     decrement&lt;br /&gt;
&lt;br /&gt;
    rjmp     main&lt;br /&gt;
&lt;br /&gt;
increment:&lt;br /&gt;
    inc      code&lt;br /&gt;
    rjmp     update&lt;br /&gt;
&lt;br /&gt;
decrement:&lt;br /&gt;
    dec      code&lt;br /&gt;
&lt;br /&gt;
update:&lt;br /&gt;
    rcall    lcd_home&lt;br /&gt;
    mov      temp1, code&lt;br /&gt;
    rcall    lcd_number&lt;br /&gt;
    ldi      temp1, &#039; &#039;&lt;br /&gt;
    rcall    lcd_data&lt;br /&gt;
    mov      temp1, code&lt;br /&gt;
    rcall    lcd_number_hex&lt;br /&gt;
    ldi      temp1, &#039; &#039;&lt;br /&gt;
    rcall    lcd_data&lt;br /&gt;
    mov      temp1, code&lt;br /&gt;
    rcall    lcd_data&lt;br /&gt;
&lt;br /&gt;
    rjmp     main&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://digital-diy.com/General-Electronics/10-keys-on-one-port-pin.html 10 Keys on One Port Pin?] - Eine ganz andere Art der Tastenerkennung über einen [[ADC]]. Damit lässt sich auch eine von vielen Tasten an nur einem ADC-Eingangspin abfragen.&lt;br /&gt;
* [http://www.openmusiclabs.com/learning/digital/input-matrix-scanning/ Input Matrix Scanning] von Open Music Labs. Tutorials und Kostenbetrachtung für 12 Verfahren, um eine Eingabematrix (1-128 Schalter an 1-20 Pins) abzufragen.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=ADC|&lt;br /&gt;
zurücklink=AVR-Tutorial: ADC|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=PWM|&lt;br /&gt;
vorlink=AVR-Tutorial: PWM}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Tasten]]&lt;/div&gt;</summary>
		<author><name>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=62967</id>
		<title>AVR-Tutorial: Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Timer&amp;diff=62967"/>
		<updated>2012-01-02T20:19:04Z</updated>

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

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

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

		<summary type="html">&lt;p&gt;Mkleemann: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Der Flash-Speicher ist Word-weise organisiert.&lt;br /&gt;
&lt;br /&gt;
Während es keine größeren Probleme bei der SRAM- und EEPROM - Programmierung&lt;br /&gt;
gibt (Byte-orientiert), stellt sich immer wieder die Frage, warum Adressen&lt;br /&gt;
im Flash mit zwei multipliziert werden müssen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: .db 0 ;  führt zu einem Warning&lt;br /&gt;
          .db 0,0&lt;br /&gt;
&lt;br /&gt;
Hier ergänzt der Assembler nach der ersten 0 eine weitere 0, um&lt;br /&gt;
auf geradzahlige Word-Grenzen zu kommen.&lt;br /&gt;
&lt;br /&gt;
Der Assembler des AVR-Studios legt immer den Zeilenanfang einer .db-Definition&lt;br /&gt;
auf eine Word - Adresse (gerade Byte-Adresse). Wenn man jetzt den Flash - Speicher auslesen möchte, muß man natürlich dann die Markenadresse mit zwei multiplizieren, da die Markenadresse immer auf einer Word - Grenze liegt, &lt;br /&gt;
um mit&lt;br /&gt;
&lt;br /&gt;
z.&amp;amp;nbsp;B.&lt;br /&gt;
    LMP R16,Z+ ;    ein Byte aus dem Flash auslesen zu können.&lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Byte läßt sich dann, ohne die Word - Grenzen zu beachten,&lt;br /&gt;
mit z.&amp;amp;nbsp;B.&lt;br /&gt;
&lt;br /&gt;
    LMP R16,Z ;    auslesen.&lt;br /&gt;
&lt;br /&gt;
Der Zeiger Z wurde in diesem Beispiel nur um eins erhöht und zeigt somit&lt;br /&gt;
zwischen die Word-Grenzen.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie: AVR-Tutorial|Unterschied Adressierung]]&lt;/div&gt;</summary>
		<author><name>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=62963</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=62963"/>
		<updated>2012-01-02T20:16:43Z</updated>

		<summary type="html">&lt;p&gt;Mkleemann: /* Literatur */&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 4 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 zb 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 Konstante. Das Ergebnis des Vergleiches wird in den Flags abgelegt. Die bedingten Sprungbefehle werten die Flags aus und führen bei einem positiven Ergebnis den Sprung aus. Besonders der erste Satzteil ist wichtig! Den bedingten Sprungbefehlen ist es nämlich völlig egal, ob die Flags über Vergleichsbefehle oder über sonstige Befehle gesetzt wurden. Die Sprungbefehle werten einfach nur die Flags aus, wie auch immer diese zu ihrem Zustand kommen.&lt;br /&gt;
&lt;br /&gt;
==Flags==&lt;br /&gt;
&lt;br /&gt;
Die Flags 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. 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;
&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;
===Zero (Z)===&lt;br /&gt;
Das Zero Flag hält fest, ob das Ergebnis der letzten 8-Bit Berechnung 0 war oder nicht.&lt;br /&gt;
&lt;br /&gt;
===Negative (N)===&lt;br /&gt;
Spiegelt den Zustand des höchstwertigen Bits (Bit 7) der letzten 8-Bit-Berechnung wieder. In 2-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 2-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 2-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 &amp;amp;ndash; wenn er vorhanden ist &amp;amp;ndash; 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 XOR-Verknüpfung aus den Ü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 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 &#039;Signed&#039; Tests benötigt. Daher auch der Name.&lt;br /&gt;
&lt;br /&gt;
===Half Carry (H)===&lt;br /&gt;
Das Half Carry Flag hat die gleiche Aufgabe wie das Carry Flag, nur beschäftigt es sich mit einem Überlauf von Bit 3 nach Bit 4, also dem Übertrag zwischen dem oberen und 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, bei der jeweils 4 Bits eine Stelle einer Dezimalzahl repräsentieren.&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 durchgeführt. Das eigentliche Ergebnis der Subtraktion wird allerdings verworfen, es bleibt nur die neue Belegung der Flags übrig, die in weiterer Folge ausgewertet werden kann&lt;br /&gt;
&lt;br /&gt;
===CP - Compare===&lt;br /&gt;
Vergleicht den Inhalt zweier Register miteinander. Prozessorintern wird dabei eine Subtraktion der beiden Register durchgeführt. Das eigentliche Subtraktionsergebnis wird allerdings verworfen, das Subtraktionsergebnis beeinflusst lediglich die Flags.&lt;br /&gt;
&lt;br /&gt;
===CPC - Compare with Carry===&lt;br /&gt;
Vergleicht den Inhalt zweier Register, wobei das Carry Flag in den Vergleich mit einbezogen wird. Dieser Befehl wird für Arithmetik mit großen Variablen (16/32 Bit) benötigt. Siehe [[AVR-Tutorial: Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===CPI - Compare Immediate===&lt;br /&gt;
Vergleicht den Inhalt eines Registers mit einer direkt angegebenen Konstanten. Der Befehl ist nur auf die Register r16..r31 anwendbar.&lt;br /&gt;
&lt;br /&gt;
==Bedingte Sprünge==&lt;br /&gt;
&lt;br /&gt;
Die bedingten Sprünge werten immer bestimmte Flags im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039; aus. Es spielt dabei keine Rolle, ob dies nach einem Vergleichsbefehl oder einem sonstigen Befehl gemacht wird. Entscheidend ist einzig und alleine der Zustand des abgefragten Flags. Die Namen der Sprungbefehle wurden allerdings so gewählt, daß sich im Befehlsnamen die Beziehung der Operanden direkt nach einem Compare Befehl wiederspiegelt. Zu beachten ist auch, daß die Flags nicht nur durch Vergleichsbefehle verändert werden, sondern auch durch arithmetische Operationen, Schiebebefehle und logische [[Bitmanipulation | Verknüpfungen]]. Da dieses Information wichtig ist, ist auch in der bei Atmel erhältlichen [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf Übersicht über alle Assemblerbefehle] bei jedem Befehl angegeben, ob und wie er Flags beeinflusst. Ebenso ist dort eine kompakte Übersicht aller bedingten Sprünge zu finden. Beachten muss man jedoch, dass die bedingten Sprünge maximal 64 Worte weit springen können.&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenlose Zahlen ===&lt;br /&gt;
&lt;br /&gt;
;BRSH - Branch if Same or Higher: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 eingesetzt, so findet der Sprung dann und nur dann statt, wenn der zweite Operand größer oder gleich dem ersten Operanden ist.&lt;br /&gt;
&lt;br /&gt;
;BRLT - Branch if Less Than: 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 zweite Operand kleiner als der erste 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 (einschieß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 grossen Variablen (16/32 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;
;BRIS - Branch if Global Interrupt is Enabled (Set): 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 beispielweise die Aufgabe vor, daß Register r18 auf 0 gesetzt werden soll, wenn 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;avrasm&amp;gt;&lt;br /&gt;
    cpi     r17, 25         ; vergleiche r17 mit der Konstante 25&lt;br /&gt;
    brne    nicht_gleich    ; wenn nicht gleich, dann mach bei nicht_gleich weiter&lt;br /&gt;
    ldi     r18, 0          ; hier stehen nun Anweisungen für den Fall&lt;br /&gt;
                            ; dass R17 gleich 25 ist&lt;br /&gt;
    rjmp    weiter          ; meist will man den anderen 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;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In ähnlicher Weise können die anderen bedingten Sprungbefehle eingesetzt werden, um die üblicherweise vorkommenden Vergleiche auf Gleichheit, Ungleichheit, Größer, Kleiner zu realisieren.&lt;br /&gt;
&lt;br /&gt;
===Schleifenkonstrukte===&lt;br /&gt;
&lt;br /&gt;
Ein immer wiederkehrendes Muster in der Programmierung ist eine &#039;&#039;&#039;Schleife&#039;&#039;&#039;. Die einfachste Form einer Schleife ist die &#039;&#039;&#039;Zählschleife&#039;&#039;&#039;. Dabei wird ein Register von einem Startwert ausgehend eine gewisse Anzahl erhöht, bis ein Endwert erreicht wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi     r17, 10         ; der Startwert sei in diesem Beispiel 10&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    inc     r17             ; erhöhe das Zaehlregister&lt;br /&gt;
    cpi     r17, 134        ; mit dem Endwert vergleichen&lt;br /&gt;
    brne    loop            ; und wenn der Endwert noch nicht erricht ist&lt;br /&gt;
                            ; wird bei der Marke loop ein weiterer Schleifendurchlauf ausgeführt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sehr oft ist es auch möglich das Konstrukt umzudrehen. Anstatt von einem Startwert aus zu inkrementieren genügt es die Anzahl der gewünschten Schleifendurchläufe in ein Register zu laden und dieses Register zu dekrementieren. Dabei kann man von der Eigenschaft der Dekrementieranweisung gebrauch machen, das &#039;&#039;&#039;Zero Flag (Z)&#039;&#039;&#039; zu beeinflussen. Ist das Ergebnis des Dekrements 0, so wird das &#039;&#039;&#039;Zero Flag (Z)&#039;&#039;&#039; gesetzt, welches wiederum in der nachfolgenden &#039;&#039;&#039;BRNE&#039;&#039;&#039; Anweisung für einen bedingen Sprung benutzt werden kann. Das vereinfacht die Schleife und spart eine Anweisung sowie einen Takt Ausführungzeit.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi     r17, 124        ; Die Anzahl der Wiederholungen in ein Register laden&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    dec     r17             ; Schleifenzähler um 1 verringern, dabei wird das Zero Flag beeinflusst&lt;br /&gt;
    brne    loop            ; wenn r17 noch nicht 0 geworden ist -&amp;gt; Schleife wiederholen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Literatur==&lt;br /&gt;
[http://atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Instruction Set]&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>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Watchdog&amp;diff=62962</id>
		<title>AVR-Tutorial: Watchdog</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Watchdog&amp;diff=62962"/>
		<updated>2012-01-02T20:15:54Z</updated>

		<summary type="html">&lt;p&gt;Mkleemann: /* Weblinks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel ist im Entstehen: Die Diskussion wird in [http://www.mikrocontroller.net/topic/131121] geführt.&lt;br /&gt;
&lt;br /&gt;
Der [[Watchdog]] im AVR (WDT) ist ein spezieller Timer, der nach Ablauf (typisch ein paar ms) automatisch einen RESET auslöst. Im Normalbetrieb wird der Watchdog in der Hauptschleife des Programms regelmäßig zurückgesetzt. Wenn durch einen Fehler dieses Zurücksetzen nicht mehr stattfindet, läuft der Watchdog-Timer ab und löst einen RESET aus, um den Mikrocontroller und damit das Programm neu zu starten.&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
(nach Ganssle-03)&lt;br /&gt;
&lt;br /&gt;
Der Watchdog ist im Prinzip zur Stelle, wenn kein Anwender da ist, um den Resetknopf zu drücken. &lt;br /&gt;
&lt;br /&gt;
Der Watchdog bringt dabei das System aus einem unvorhergesehenen Fehlerzustand wieder in einen betriebsbereiten Zustand.&lt;br /&gt;
&lt;br /&gt;
Dieser Zustand nach einem WDT Reset kann je nach Implementierung im Programm sein:&lt;br /&gt;
&lt;br /&gt;
* Debugzustand&lt;br /&gt;
* Sicherheitszustand&lt;br /&gt;
* Betriebszustand &lt;br /&gt;
&lt;br /&gt;
Den Debugzustand kann man während der Entwicklung nutzen, um unvorhergesehene Ereignisse herauszufinden. Im fertigen System sollten diese durch das Debuggen bekannten Ereignisse korrekt, d.h. nicht über WDT behandelt werden.&lt;br /&gt;
&lt;br /&gt;
Den Sicherheitszustand kann man verwenden, wenn das System aufgrund von Hardwareproblemen den WDT ausgelöst hat. Nach dem Reset durch den WDT wird die Resetquelle (normaler Reset oder WDT Reset?) ausgewertet und das System/die Hardware geprüft und ggf. in eine sichere Konfiguration statt in den normalen Betrieb gebracht.&lt;br /&gt;
&lt;br /&gt;
Der normale Betriebszustand ist im Prinzip ein Sonderfall des Sicherheitszustands. Es ist zwar ein unerwartetes Ereignis eingetreten (z.&amp;amp;nbsp;B. einzelner zufälliger Speicherlesefehler), aber ein Neustart des Programms scheint nach einer Neuinitialisierung möglich. Ein Sonderfall ist die Anwendung des WDT zum bewussten Reset (s. Tipps &amp;amp; Tricks).&lt;br /&gt;
&lt;br /&gt;
== Steuerung ==&lt;br /&gt;
&lt;br /&gt;
Der WDT wird durch das Watchdog Timer Control Register WDTCR gesteuert.&lt;br /&gt;
&lt;br /&gt;
{{ByteNumbered | WDTCR&lt;br /&gt;
 | — | — | — &lt;br /&gt;
 | WDCE&amp;lt;ref&amp;gt;Watch Dog Change Enable; heißt bei ATmega16/32: WDTOE – Watchdog Turn Off Enable&amp;lt;/ref&amp;gt;&lt;br /&gt;
 | WDE&amp;lt;ref&amp;gt;Watch Dog Enable&amp;lt;/ref&amp;gt;&lt;br /&gt;
 | WDP2&amp;lt;ref name=&amp;quot;WDP&amp;quot;&amp;gt;Watch Dog Timer Prescaler, bit x&amp;lt;/ref&amp;gt;&lt;br /&gt;
 | WDP1&amp;lt;ref name=&amp;quot;WDP&amp;quot;/&amp;gt;&lt;br /&gt;
 | WDP0&amp;lt;ref name=&amp;quot;WDP&amp;quot;/&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| {{Tabelle}}&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc;&amp;quot;&lt;br /&gt;
! WDP2 || WDP1 || WDP0 || Time-out nach [ms]&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0 || 0 || 0&lt;br /&gt;
| &amp;amp;nbsp;&amp;amp;nbsp;16,3&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0 || 0 || 1&lt;br /&gt;
| &amp;amp;nbsp;&amp;amp;nbsp;32,5&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0 || 1 || 0&lt;br /&gt;
| &amp;amp;nbsp;&amp;amp;nbsp;65&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0 || 1 || 1&lt;br /&gt;
| &amp;amp;nbsp;130&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1 || 0 || 0&lt;br /&gt;
| &amp;amp;nbsp;260&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1 || 0 || 1&lt;br /&gt;
| &amp;amp;nbsp;520&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1 || 1 || 0&lt;br /&gt;
| 1100&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1 || 1 || 1&lt;br /&gt;
| 2100&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
;Achtung: Die Zeiten sind abhängig von der Betriebspannung und der Taktfrequenz des WDT-Oszillators. Sie sollten daher aus dem jeweiligen Datenblatt des µCs entnommen werden.&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
=== WDT durch WDTON-Fuse aktivieren ===&lt;br /&gt;
&lt;br /&gt;
Am Einfachsten läßt es sich durch ein kleines Programmbeispiel demonstrieren.&lt;br /&gt;
&lt;br /&gt;
Ein ATmega8 wird mit 4 MHz des internen Taktgenerators mit einer Startup-Zeit von 64 ms getaktet. Die WDTON-Fuse ist gesetzt (WDT aktiviert). &lt;br /&gt;
An Port B ist eine LED angeschlossen (Pin egal).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
; ATMega8L mit internen 4 MHz getaktet + 64 ms Startuptime&lt;br /&gt;
; WDTON aktiviert!&lt;br /&gt;
&lt;br /&gt;
.def Temp1 = R16&lt;br /&gt;
.def SubCount = R17&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    Reset               ; Reset Handler&lt;br /&gt;
.org OC1Aaddr&lt;br /&gt;
        rjmp    timer1_compare      ; Timer Compare Handler&lt;br /&gt;
&lt;br /&gt;
Reset:&lt;br /&gt;
        ldi     Temp1, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, Temp1&lt;br /&gt;
        ldi     Temp1, LOW(RAMEND)  ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, Temp1&lt;br /&gt;
&lt;br /&gt;
        ldi     Temp1, 0xFF         ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, Temp1&lt;br /&gt;
&lt;br /&gt;
        ldi     Temp1, high(40000 - 1)&lt;br /&gt;
        out     OCR1AH, temp1&lt;br /&gt;
        ldi     Temp1, low(40000 - 1)&lt;br /&gt;
        out     OCR1AL, temp1&lt;br /&gt;
                                    ; CTC Modus einschalten&lt;br /&gt;
                                    ; Vorteiler auf 1&lt;br /&gt;
        ldi     Temp1, ( 1 &amp;lt;&amp;lt; WGM12 ) | ( 1 &amp;lt;&amp;lt; CS10 )&lt;br /&gt;
        out     TCCR1B, temp1&lt;br /&gt;
&lt;br /&gt;
        ldi     Temp1, 1 &amp;lt;&amp;lt; OCIE1A  ; OCIE1A: Interrupt bei Timer Compare&lt;br /&gt;
        out     TIMSK, temp1&lt;br /&gt;
&lt;br /&gt;
        ; kann auch weggelasen werden, da nach einem Reset das Register&lt;br /&gt;
        ; auf 0x00 steht, WDT Reset nach 16ms&lt;br /&gt;
        ldi Temp1, (0&amp;lt;&amp;lt;WDCE)|(0&amp;lt;&amp;lt;WDE)|(0&amp;lt;&amp;lt;WDP2)|(0&amp;lt;&amp;lt;WDP1)|(0&amp;lt;&amp;lt;WDP0)&lt;br /&gt;
        out WDTCR, Temp1&lt;br /&gt;
&lt;br /&gt;
        sei&lt;br /&gt;
&lt;br /&gt;
Mainloop:&lt;br /&gt;
        rjmp    Mainloop&lt;br /&gt;
&lt;br /&gt;
timer1_compare:                     ; Timer 1 Output Compare Handler&lt;br /&gt;
&lt;br /&gt;
        ;** findet 100 x pro  Sekunde statt (10 ms)&lt;br /&gt;
        wdr                         ; Watch-Dog-Reset&lt;br /&gt;
&lt;br /&gt;
        inc     SubCount            ; Wenn dies nicht der 50. Interrupt&lt;br /&gt;
        cpi     SubCount, 50        ; ist, dann passiert gar nichts&lt;br /&gt;
        brne    exit_isr&lt;br /&gt;
&lt;br /&gt;
        ;** findet 2 x pro  Sekunde statt (500ms)&lt;br /&gt;
        clr     SubCount&lt;br /&gt;
&lt;br /&gt;
        ;** Port B negieren&lt;br /&gt;
        in      Temp1, PinB&lt;br /&gt;
        com     Temp1&lt;br /&gt;
        out     PortB, Temp1&lt;br /&gt;
&lt;br /&gt;
exit_isr:&lt;br /&gt;
        reti                        ; das wars. Interrupt ist fertig&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Timer 1 läuft im CTC-Modus mit einer Frequenz von 100 Hz (10 ms).&lt;br /&gt;
Durch den Soft-Subcounter wird die Frequenz auf 2 Hz geteilt und jeweils nach 500 ms das Port B negiert. &lt;br /&gt;
&lt;br /&gt;
Da die LED in diesem Beispiel nach 500 ms jeweils ein- und ausgeschaltet wird, blinkt sie mit einer Frequenz von 1 Hz. Der WDT wird nach 10 ms zurückgesetzt, so dass er keinen RESET auslösen kann.&lt;br /&gt;
&lt;br /&gt;
Wird jetzt der Befehl WDR auskommentiert, führt der WDT nach 16 ms&lt;br /&gt;
einen RESET aus. Die LED blinkt nun, bedingt durch die Startup-Zeit&lt;br /&gt;
von 64 ms und einem Time-out von 16ms, mit rund 6 Hz.&lt;br /&gt;
1/(64ms + 16ms) ~ 12 Hz (halbe Periode)&lt;br /&gt;
&lt;br /&gt;
=== WDT durch Software aktivieren/deaktivieren ===&lt;br /&gt;
&lt;br /&gt;
Der WDT läßt sich auch softwaremäßig durch Setzen des WDE-Bits im WDTCR Register aktivieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
WDT_on:&lt;br /&gt;
&lt;br /&gt;
  in Temp1, WDTCR      ; Write logical one to WDE&lt;br /&gt;
  ori Temp1, (1&amp;lt;&amp;lt;WDE)&lt;br /&gt;
  out WDTCR, Temp1&lt;br /&gt;
  ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses hat den Vorteil, dass man den WDT auch softwaremäßig wieder deaktivieren kann.&lt;br /&gt;
&lt;br /&gt;
Ein Deaktivieren des WDTs ist nicht möglich, wenn die WDTON - Fuse&lt;br /&gt;
gesetzt ist!&lt;br /&gt;
&lt;br /&gt;
Das softwaremäßige Deaktivieren verlangt allerdings eine besondere&lt;br /&gt;
Deaktivierungssequenz. (in drei Phasen)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
WDT_off:&lt;br /&gt;
&lt;br /&gt;
  ;** 1. Phase&lt;br /&gt;
  wdr                   ; reset WDT&lt;br /&gt;
&lt;br /&gt;
  ;** 2. Phase&lt;br /&gt;
  in  Temp1, WDTCR      ; Write logical one to WDCE and WDE&lt;br /&gt;
  ori Temp1, (1&amp;lt;&amp;lt;WDCE)|(1&amp;lt;&amp;lt;WDE)&lt;br /&gt;
&lt;br /&gt;
  in  Temp2, SREG       ; save I Flag&lt;br /&gt;
  cli                   ; we have only 5 cycles to reset WDE &lt;br /&gt;
&lt;br /&gt;
  out WDTCR, Temp1&lt;br /&gt;
&lt;br /&gt;
  ;** 3. Phase&lt;br /&gt;
  ldi Temp1, (0&amp;lt;&amp;lt;WDE)   ; Turn off WDT&lt;br /&gt;
  out WDTCR, Temp1&lt;br /&gt;
  &lt;br /&gt;
  out SREG, Temp2       ; restore I Flag   &lt;br /&gt;
  ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn WDCE und WDE nicht in einem Zug vor dem Deaktivieren auf 1 gesetzt werden, hat das Rücksetzen des WDE-Bits keine Wirkung und der WDT läuft munter weiter!&lt;br /&gt;
&lt;br /&gt;
Dazu hat man maximal 5 Takte Zeit. Diese Sequenz darf auch nicht durch einen Interrupt unterbrochen werden.&lt;br /&gt;
&lt;br /&gt;
== Tipps &amp;amp; Tricks ==&lt;br /&gt;
&lt;br /&gt;
=== WDT nach einem Reset ===&lt;br /&gt;
&lt;br /&gt;
Der WDT bleibt bei manchen AVRs nach einem Reset (ob durch den Watchdog, extern oder aus sonstigen Gründen, also auch über das Flashen einer neuen Software hinweg!) aktiv,&lt;br /&gt;
wenn er einmal an war. Er läuft danach mit der kürzesten Zeit weiter, da die Prescaler Bits beim Reset gelöscht werden und somit die Watchdog Zeit auf die kürzeste Zeit gesetzt wird. Das kann zu unerwarteten Problemen führen ([http://www.mikrocontroller.net/topic/131137])&lt;br /&gt;
&lt;br /&gt;
Dies steht nicht explizit im Datenblatt, sondern man kann es nur&lt;br /&gt;
anhand der Defaultwerte der Bits entnehmen, was viele übersehen.&lt;br /&gt;
Dies ist vor allem beim Programmieren in einer Hochsprache wie C wichtig, denn da verwendet man meist Makros für den Watchdog und kommt somit nicht direkt mit den Registern aus dem Datenblatt in Berührung. Weiterhin dauert die Initialisierung der globalen Variablen vor dem Start der main Funktion oft länger als die Watchdog Periode, was dazu führt, dass die main Funktion nie erreicht wird.&lt;br /&gt;
Der Watchdog Timer muss daher vorher abgeschaltet werden, was beim gcc über Code in einer speziellen Section geschieht, die unmittelbar nach dem Reset ausgeführt wird.&lt;br /&gt;
Ein Beispiel findet sich dazu in folgendem Thread:&lt;br /&gt;
[http://www.mikrocontroller.net/topic/130969#1195920]&lt;br /&gt;
&lt;br /&gt;
=== WDT gezielt zum Reset verwenden ===&lt;br /&gt;
&lt;br /&gt;
Man kann den WDT auch verwenden, um gezielt per Software einen Reset des AVR auszulösen. Das wird z.&amp;amp;nbsp;B. im [[AVR Bootloader FastBoot von Peter Dannegger]] gemacht.&lt;br /&gt;
&lt;br /&gt;
Variante1: Ohne Unterbrechung des Programmablaufs über eine Statusvariable oder Statusflag, z.B:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	tst rFlag&lt;br /&gt;
	brne PC+2&lt;br /&gt;
	wdr&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Um ein WDT-Reset auszulösen wird das Register rFlag an beliebiger Stelle auf ein Wert ungleich Null gesetzt. &lt;br /&gt;
&lt;br /&gt;
Variante 2: Programm anhalten und auf WDT-Reset warten&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	rjmp PC&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Resetquelle auswerten ===&lt;br /&gt;
&lt;br /&gt;
Nach einem WDT-Reset wird die gleiche Adresse (0x0000) angesprungen, wie nach einem normalen Reset. Zur Unterscheidung der Reset-Quelle ist eine Auswertung des WDRF-Flags im MCU Control -und Statusregister erforderlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	in Temp1,MCUCSR		        ;MCU Control- und Statusregister lesen&lt;br /&gt;
	sbrs Temp1,WDRF		        ;Prüfen ob Watchdog-Restart erfolgte&lt;br /&gt;
	rjmp normalReset		;nein: Normalstart&lt;br /&gt;
	cbr Temp1,1&amp;lt;&amp;lt;WDRF		;Watchdog-Resetflag zurücksetzen&lt;br /&gt;
	out MCUCSR, Temp1&lt;br /&gt;
	rjmp watchDogReset		;WDRStart&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Aufwecken aus einem Sleep Mode ===&lt;br /&gt;
Der WDT kann auch verwendet werden, um einen AVR im Rahmen der möglichen WDT-Zeiten zeitgesteuert aus einem [[Sleep Mode]] aufzuwecken. Allerdings verbraucht der eingeschaltete WDT einen gewissen Strom [http://www.mikrocontroller.net/topic/152298]. Beispiel in C: [[Pollin_Funk-AVR-Evaluationsboard#Pennen_bis_der_Hund_bellt|Pollin Funk-AVR-Evaluationsboard: Pennen bis der Hund bellt]].&lt;br /&gt;
&lt;br /&gt;
=== WDTON Fuse zurücksetzen ===&lt;br /&gt;
&lt;br /&gt;
Die Änderung der WDTON Fuse wird erst nach einer&lt;br /&gt;
Spannungsunterbrechung wirksam. Ein Reset ist nicht ausreichend.&lt;br /&gt;
[http://www.mikrocontroller.net/topic/185059#1794201]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.embedded.com/columns/breakpoint/9900931?_requestid=140114 Li&#039;l Bow Wow] By Jack Ganssle, Embedded Systems Design (engl.)&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/95973 Watchdog Reset Flag auswerten] Erklärung, wie das WDRF unter C abgefragt wird&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Servos|&lt;br /&gt;
zurücklink=AVR-Tutorial: Servo|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Power Management|&lt;br /&gt;
vorlink=AVR-Tutorial: Power Management}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Watchdog]]&lt;/div&gt;</summary>
		<author><name>Mkleemann</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Interrupts&amp;diff=62961</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=62961"/>
		<updated>2012-01-02T20:15:23Z</updated>

		<summary type="html">&lt;p&gt;Mkleemann: /* Siehe auch */&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;&amp;lt;i&amp;gt;Interrupt&amp;lt;/i&amp;gt;&#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.&lt;br /&gt;
&lt;br /&gt;
==INT0, INT1 und die zugehörigen Register==&lt;br /&gt;
&lt;br /&gt;
Wir wollen uns hier erst mal 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) und &#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;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}}&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! ISCx1 || ISCx0 || 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;
&amp;lt;/center&amp;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 &#039;&#039;&#039;cbi&#039;&#039;&#039; und &#039;&#039;&#039;sbi&#039;&#039;&#039; 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 &amp;quot;m8def.inc&amp;quot;, und im Datenblatt des Controllers). Somit bleiben zum Zugriff auf diese Register nur die Befehle &#039;&#039;&#039;in&#039;&#039;&#039; und &#039;&#039;&#039;out&#039;&#039;&#039; ü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;sei&#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;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}}&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&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;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So, wir wissen jetzt, dass der Controller zu Adresse 0x001 springt, wenn &#039;&#039;&#039;INT0&#039;&#039;&#039; 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. &#039;&#039;&#039;rjmp interrupt0&#039;&#039;&#039;. Irgendwo anders im Programm muss in diesem Fall eine Stelle mit &amp;lt;i&amp;gt;interrupt0:&amp;lt;/i&amp;gt; gekennzeichnet sein, zu der dann gesprungen wird. Diese durch den Interrupt aufgerufene Routine nennt man &#039;&#039;&#039;&amp;lt;i&amp;gt;Interrupthandler&amp;lt;/i&amp;gt;&#039;&#039;&#039; (engl. &#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;).&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;reti&#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 &#039;&#039;&#039;reti&#039;&#039;&#039; und nicht ein normaler &#039;&#039;&#039;ret&#039;&#039;&#039; benutzt wird. Wird ein Interrupt Handler betreten, so sperrt der Mikrocontroller automatisch alle weiteren Interrupts. Im Unterschied zu &#039;&#039;&#039;ret&#039;&#039;&#039;, hebt ein &#039;&#039;&#039;reti&#039;&#039;&#039; 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 &#039;&#039;&#039;rjmp interrupt0&#039;&#039;&#039; an die richtige Stelle im Programmspeicher schreibt, nämlich an den Interruptvektor für &#039;&#039;&#039;INT0&#039;&#039;&#039;. Dazu gibt es eine Assemblerdirektive. Durch &#039;&#039;&#039;.org 0x001&#039;&#039;&#039; sagt man dem Assembler, dass er die darauffolgenden Befehle ab Adresse 0x001 im Programmspeicher platzieren soll. Diese Stelle wird von &#039;&#039;&#039;INT0&#039;&#039;&#039; 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;i&amp;gt;INT0addr&amp;lt;/i&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;rjmp RESET&#039;&#039;&#039; um an die mit &#039;&#039;&#039;&amp;lt;i&amp;gt;RESET:&amp;lt;/i&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 .org an die richtige Stelle zu rücken, die gesamte Sprungtabelle ausschreiben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.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;/avrasm&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;reti&#039;&#039;&#039; (bzw. &#039;&#039;&#039;reti nop&#039;&#039;&#039;, wenn jmp 4 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 .org adressieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.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 SPMaddr                  ; 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;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt die unbenutzten Interruptvektoren mit &#039;&#039;&#039;reti&#039;&#039;&#039; zu füllen könnte man sie hier auch einfach weglassen, da die &#039;&#039;&#039;.org&#039;&#039;&#039;-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 Interrupt Pins können zb 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;
[http://www.mikrocontroller.net/sourcecode/tutorial/extinttest.asm Download extinttest.asm]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
&lt;br /&gt;
.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;/avrasm&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 Kapitel über den [[AVR-Tutorial:_UART|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;&amp;lt;i&amp;gt;Status Register&amp;lt;/i&amp;gt;&#039;&#039;&#039;. In ihm merkt sich der Prozessor bestimmte Zustände von Berechnungen, z. 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 &#039;&#039;&#039;SREG&#039;&#039;&#039; Register 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 &#039;&#039;&#039;SREG&#039;&#039;&#039; Registers 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 bischen Rechenzeit. Im anderen Fall handelt man sich allerdings einen Super-GAU ein: Man steht dann vor einem Programm, das sporadisch nicht funktioniert und keiner weiss 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 &#039;&#039;&#039;temp&#039;&#039;&#039; und &#039;&#039;&#039;SREG&#039;&#039;&#039; nicht wirklich notwendig, aber hier soll die grundsätzliche Vorgehensweise gezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp = r16&lt;br /&gt;
 &lt;br /&gt;
.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;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
[[Interrupt | Interrupt: Anderer Wiki-Artikel über Interrupts]]&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>Mkleemann</name></author>
	</entry>
</feed>