<?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=134.106.243.22</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=134.106.243.22"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/134.106.243.22"/>
	<updated>2026-04-11T03:16:33Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Simulation&amp;diff=80865</id>
		<title>AVR-Simulation</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Simulation&amp;diff=80865"/>
		<updated>2014-01-15T12:40:41Z</updated>

		<summary type="html">&lt;p&gt;134.106.243.22: /* AVR Studio */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
== SimulAVR ==&lt;br /&gt;
&lt;br /&gt;
[http://www.nongnu.org/simulavr/ SimulAVR] ist ein Simulator, der einige Prozessoren der AVR-Familie auf einem PC-Hostsystem (Windows, Linux, Mac OS X) simulieren kann. Dieser Simulator ist auch im [[WinAVR]]-Paket enthalten (/WinAVR/bin/simulavr.exe).&lt;br /&gt;
&lt;br /&gt;
Eine Verwendung von SimulAVR ist das sog. Signal Tracing, d.h. das aufzeichnen von Änderungen der diversen Register (z.B. der Pins eines I/O-Ports) in eine VCD-Datei. Auch wenn die Laufzeit des Simulators oft langsamer als die der Hardware ist, entsprechen diese Aufzeichnungen auf den CPU-Takt genau dem, was auf der Hardware passieren würde. Wie diese Aufzeichnungen gemacht werden ist bei SimulAVR selbst kaum dokumentiert, doch bei RepRap gibt es eine kleine [http://reprap.org/wiki/SimulAVR#Signal_tracing SimulAVR Anleitung], die eine gute Passage dazu enthält.&lt;br /&gt;
&lt;br /&gt;
Die Hauptanwendung von SimulAVR dürfte das Debuggen sein. Dafür wird SimulAVR über ein dazu passendes Frontend bedient. GDB und Insight sind zwei, ebenfalls in WinAVR enthaltene, Programme, die ein solches Frontend bilden. &lt;br /&gt;
&lt;br /&gt;
Wer &amp;quot;textorientiertes&amp;quot; Debugging mag, oder beispielsweise &#039;&#039;emacs&#039;&#039; als Frontend verwenden möchte, der nutzt den [[GDB]], den GNU Debugger. Er ist im WinAVR-Paket enthalten (/WinAVR/bin/avr-gdb.exe). Wer gleich eine grafische Benutzeroberfläche (GUI) verwenden möchte, nutzt Insight (welches intern ebenfalls den GDB verwendet). &lt;br /&gt;
&lt;br /&gt;
Beide Debugger, also GDB oder Insight, benötigen ein Backend. Entweder handelt es sich dabei um einen ICE/[[JTAG]]-Debugger wie z.&amp;amp;nbsp;B. [http://avarice.sourceforge.net/ AVaRICE] als Interface zu einem realen AVR-Mikrocontroller oder um einen Simulator.&lt;br /&gt;
&lt;br /&gt;
=== Starten von SimulAVR ===&lt;br /&gt;
&lt;br /&gt;
[[Datei:SimulAVR-Display.png|right|220px]]&lt;br /&gt;
Um SimulAVR mit default-Parametern zu starten, reicht es aus, folgende Zeile in einer Shell/Eingabeaufforderung einzugeben:&lt;br /&gt;
&lt;br /&gt;
 $ simulavr -g -d &amp;lt;AVR-Prozessortyp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SimulAVR startet dann mit den defaultparametern und stellt einen GDBServer-Prozess auf dem lokalen oder entfernten Host auf Port 1212 zur Verfügung.&lt;br /&gt;
Das &#039;&#039;Backend&#039;&#039; der Simulation ist nun Einsatzbereit.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit ist, SimulAVR mit Display zu starten. Dann werden während der Ausführung die Werte der Register und dem RAM angezeigt (siehe Bild).&lt;br /&gt;
&lt;br /&gt;
 $ simulavr -g -P simulavr-disp -d &amp;lt;AVR-Prozessortyp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Übersicht der unterstützten AVR-Prozessoren und der SimulAVR spezifischen Parameter bekommt man durch folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
 $ simulavr --help&lt;br /&gt;
&lt;br /&gt;
=== Starten des GDB ===&lt;br /&gt;
&lt;br /&gt;
Um das (vorläufige) Frontend GDB zu starten, reicht folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
 $ avr-gdb&lt;br /&gt;
&lt;br /&gt;
Der GDB meldet sich mit folgendem Bildschirm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Current directory is c:/WinAVR/testprj4/&lt;br /&gt;
GNU gdb 6.1&lt;br /&gt;
Copyright 2004 Free Software Foundation, Inc.&lt;br /&gt;
GDB is free software, covered by the GNU General Public License, and you are&lt;br /&gt;
welcome to change it and/or distribute copies of it under certain conditions.&lt;br /&gt;
Type &amp;quot;show copying&amp;quot; to see the conditions.&lt;br /&gt;
There is absolutely no warranty for GDB.  Type &amp;quot;show warranty&amp;quot; for details.&lt;br /&gt;
This GDB was configured as &amp;quot;--host=i686-pc-cygwin --target=avr&amp;quot;.&lt;br /&gt;
(gdb) &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(gdb)&#039;&#039;&#039; ist der Prompt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Soll während dem Debuggen der Quellcode angezeigt werden, der zur Zeit ausgeführt wird, kann GDB wie folgt gestartet werden.&lt;br /&gt;
&lt;br /&gt;
 $ avr-gdb -tui&lt;br /&gt;
&lt;br /&gt;
Beim (gdb) prompt dann das Layout auf Assembler umschalten.,&lt;br /&gt;
&lt;br /&gt;
 (gdb) layout asm&lt;br /&gt;
&lt;br /&gt;
=== Simulieren/Debuggen ===&lt;br /&gt;
&lt;br /&gt;
Im ersten Schritt muss dem GDB die gewünschte Datei (vorher mittels entsprechendem &#039;&#039;Makefile&#039;&#039; erzeugt) bekannt gemacht werden. Es ist darauf zu achten, dass das Programm mit Debugsymbolen kompiliert wurde.&lt;br /&gt;
&lt;br /&gt;
 (gdb) file main.elf &lt;br /&gt;
 Reading symbols from main.elf...done.&lt;br /&gt;
&lt;br /&gt;
Als nächstes muss dem GDB mitgeteilt werden, welches Ziel (target) er als Backend verwenden soll:&lt;br /&gt;
&lt;br /&gt;
 (gdb) target remote localhost:1212&lt;br /&gt;
&lt;br /&gt;
oder kürzer:&lt;br /&gt;
&lt;br /&gt;
 (gdb) targ rem :1212&lt;br /&gt;
 Remote debugging using :1212&lt;br /&gt;
 0x00000000 in __vectors ()&lt;br /&gt;
&lt;br /&gt;
Jetzt muss das Programm in den Simulator geladen werden:&lt;br /&gt;
&lt;br /&gt;
 (gdb) load&lt;br /&gt;
 Loading section .data, size 0x32 lma 0x1bc&lt;br /&gt;
 Loading section .text, size 0x1bc lma 0x0&lt;br /&gt;
 Start address 0x0, load size 494&lt;br /&gt;
 Transfer rate: 3952 bits in &amp;lt;1 sec, 30 bytes/write.&lt;br /&gt;
&lt;br /&gt;
Damit der Simulator nicht das gesamte Programm abarbeitet, sollte man &#039;&#039;breakpoints&#039;&#039; setzen.&lt;br /&gt;
Den Ersten am besten bei &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
 (gdb) b main&lt;br /&gt;
 Breakpoint 1 at 0xd2: file main.c, line 16.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man das Programm im Simulator mit &#039;&#039;continue&#039;&#039; starten:&lt;br /&gt;
&lt;br /&gt;
 (gdb) c&lt;br /&gt;
 Continuing.&lt;br /&gt;
&lt;br /&gt;
Bei Erreichen des zuvor gesetzten &#039;&#039;breakpoints&#039;&#039; stoppt GDB und meldet:&lt;br /&gt;
&lt;br /&gt;
 Breakpoint 1, main () at main.c:16&lt;br /&gt;
 (gdb)&lt;br /&gt;
&lt;br /&gt;
Eine Liste der verfügbaren debug-befehle des GDB findet sich durch die Eingabe von &#039;&#039;&#039;help&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[TODO] Einige häufige GDB-Commands als Beispiel einfügen.&lt;br /&gt;
&lt;br /&gt;
== VMLab ==&lt;br /&gt;
&lt;br /&gt;
[http://www.amctools.com/vmlab.htm VMLab] ist eine komplette Freeware-IDE mit Debugger und Simulator für MS-Windows. VMLab arbeitet mit [[WinAVR]] zusammen und kann AVR-Peripheriehardware simulieren. Leider hat der Autor von VMLab die Entwicklung im Jahr 2006 eingestellt, da dem Projekt nicht der gewünschte kommerzielle Erfolg vergönnt war. Die aktuelle Version ist 3.15 von Oktober 2009.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* Forumsbeitrag [http://www.mikrocontroller.net/topic/24494#714662 VMLAB Bug bei Stackpointer/Interrupt?]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/85212#716727 Workaround]&lt;br /&gt;
&lt;br /&gt;
== AVR Studio ==&lt;br /&gt;
&lt;br /&gt;
[[Bild: AVR-Studio_Simulation.png|thumb|right|250px|Simulator-Ansicht in AVR Studio]]&lt;br /&gt;
[[AVR-Studio|AVR Studio]] von Atmel enthält neben dem Übersetzungsteil, dem Debuggingteil auch einen Simulatorteil. Eine Übersicht über alle drei Programmteile gibt der Powerpoint-Vortrag [http://ece353.ecs.umass.edu/lab2/labC-lectureslides.pptx AVR Studio: The comprehensive tutorial] (MS PowerPoint &#039;&#039;2007&#039;&#039;)(link tot!) von Jeremy Gummeson und Brendan Kemp.&lt;br /&gt;
&lt;br /&gt;
Wenn nur ein Breakpoint gesetzt ist, kann mit F5 das Programm einen Abarbeitungszyklus machen. Die Bearbeitung bleibt dann wieder beim Breakpoint stehen. Mit der Stoppuhr kann dann die Zeit für den abgearbeiteten Programmteil gemessen werden. Mit der rechten Mouse-Taste kann die Stoppuhr zurückgesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ausführliche Anleitung zum Ablauf einer Simulation:  [http://www2.tech.purdue.edu/ecet/courses/ecet309/Reference_Materials/Simulation_AVR_Studio_4.pdf Tutorial der Purdue University] (engl., PDF) (link tot!)&lt;br /&gt;
&lt;br /&gt;
Externe Signale können mit den Tools HAPSIM oder über eine Stimulidatei in den Simulator eingespeist werden. &lt;br /&gt;
&lt;br /&gt;
Zu beachten ist, dass sich mit der Einführung des AVR Simulator 2, welcher  Vorteile wie Unterstützung von [[I2C|TWI]] bietet, das Datenformat für extern zugeführte Stimuli-Files geändert hat. Eine kleine Einführung inkl. Beispiel bietet die im AVR-Studio enthaltene Hilfe:&lt;br /&gt;
:AVR Studio Help → AVR Tools User Guide → Simulator → Simulator2 Stimuli&lt;br /&gt;
&lt;br /&gt;
=== HAPSIM ===&lt;br /&gt;
&lt;br /&gt;
[http://www.helmix.at/hapsim/index.htm HAPSIM] (Helmis AVR Periphery Simulator) ist ein Tool zur graphischen Simulation von Tasten, [[LED]], [[LCD]] und Terminalfunktionen in AVR Studio. (deutsch/engl., Windows, Freeware). Hinweise zur Installation von Hapsim nach einem AVR Studio Update stehen im [http://www.mikrocontroller.net/topic/118668 Forum]. HAPSIM kann nur mit AVRStudio 4.x verwendet werden, für den Fall, dass die Domain selbst nicht funktioniert kann folgender [http://members.aon.at/helmi/hapsim.htm Direktlink] verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Stimuli ===&lt;br /&gt;
&lt;br /&gt;
Über eine sog. Stimuli-Datei lassen sich von ausserhalb Signale dem AVR Studio zuführen. Informationen zum Aufbau einer Stimulidatei finden sich in obigem &amp;quot;AVR Studio: The comprehensive tutorial&amp;quot; und bei Atmel ([http://support.atmel.com/bin/customer.exe?=&amp;amp;action=viewKbEntry&amp;amp;id=46 What is the format of the AVR Studio simulator stimuli files?]). Der [http://www.hot.ee/nppcsoftware/ Stimuli Generator for AVR Studio] ist eine Freeware für die Erzeugung von Stimulidateien und deren grafischen Darstellung. Für dieses Programm ist .NET 1.1 oder 2.0 erforderlich.&lt;br /&gt;
&lt;br /&gt;
=== LogAnalyser ===&lt;br /&gt;
&lt;br /&gt;
[http://www.dresco.co.uk/LogAnalyser/ LogAnalyser] v1.00 (c) 2008 Jon Escombe is a graphical &amp;quot;logic analyser&amp;quot; style display of Atmel AVR Studio simulator log files. The project is built using the (excellent) zedGraph charting class library, and the source code is made available under GPL license. Requires Windows XP and .NET framework 2.0 to run.&lt;br /&gt;
&lt;br /&gt;
=== TCPSimDBG ===&lt;br /&gt;
&lt;br /&gt;
[http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=74350 TCPSimDBG] ist ein Plugin für [[AVR-Studio]] um Remote Simulating/Debugging über TCP/IP durchzuführen. Ein TCP/IP-Terminal oder Serverprogramm kann sich mit dem Simulator/Debugger von AVR-Studio verbinden und Informationen aus dem Debugprozess abrufen und ändern. Damit lassen sich virtuelle Geräte (Devices) eingerichten. (Atmel SDK Lizenz, Windows XP, 2003, Vista .NET 3.5 erforderlich)&lt;br /&gt;
&lt;br /&gt;
=== AVR Logic Analyzer ===&lt;br /&gt;
&lt;br /&gt;
[http://www.coynetechsystems.com/avrlogicanalyzer/index.php AVR Logic Analyzer] von Coyne Technology Systems ist ein AVR Studio Plugin zur grafischen Anzeige der simulierten I/O-Ports.&lt;br /&gt;
(kommerziell)&lt;br /&gt;
&lt;br /&gt;
== Avrora ==&lt;br /&gt;
[http://avrora.sourceforge.net/ Avrora], a research project of the UCLA Compilers Group, is a set of simulation and analysis tools for programs written for the AVR microcontroller produced by Atmel and the Mica2 sensor nodes. Avrora contains a flexible framework for simulating and analyzing assembly programs, providing a clean Java API and infrastructure for experimentation, profiling, and analysis.&lt;br /&gt;
&lt;br /&gt;
== Proteus VSM for Atmel AVR ==&lt;br /&gt;
&lt;br /&gt;
[http://www.labcenter-electronics.com/products/avr.cfm Proteus VSM for Atmel AVR] ist ein kommerzieller AVR Simulator. &lt;br /&gt;
&lt;br /&gt;
In der Codesammlung gibt es dafür ein [http://www.mikrocontroller.net/topic/118974#1072449 Virtuelles AVR Funk Pollin Simulationsmodell] von Daniel Cagara.&lt;br /&gt;
&lt;br /&gt;
== CrossWorks for AVR == &lt;br /&gt;
&lt;br /&gt;
[http://rowley.co.uk/avr/index.htm CrossWorks for AVR] besitzt einen &#039;&#039;core simulator&#039;&#039; für AVRs. (Windows, Mac OS X, Linux, Solaris)&lt;br /&gt;
&lt;br /&gt;
== simavr ==&lt;br /&gt;
&lt;br /&gt;
[http://gitorious.org/simavr simavr] is a new AVR simulator from Michel &amp;quot;buserror&amp;quot; Pollet for linux, or any platform that uses avr-gcc. It uses avr-gcc own register definition to simplify creating new targets for supported AVR devices.&lt;br /&gt;
&lt;br /&gt;
The status of the project is the core works at about 98% (ie, it works, but there is a known bug). The supported IOs are eeprom, IO ports (including pin interupts), 8 bits timers (well, one of mode of the myriad) and a simple UART that makes “printf” work.&lt;br /&gt;
&lt;br /&gt;
gdb support is planned next.&lt;br /&gt;
&lt;br /&gt;
Lizenz: GPL v3&amp;lt;br&amp;gt;&lt;br /&gt;
[http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=86665 Diskussion bei avrfreaks.net]&lt;br /&gt;
&lt;br /&gt;
== avrtest ==&lt;br /&gt;
&lt;br /&gt;
avrtest ist ein freier AVR Core-Simulator, der zur Ausführung der avr-gcc Testsuite verwendet wird. Über spezielle &amp;quot;magische SFRs&amp;quot; können bestimmte Aktionen ausgelöst werden wie z.B. definierter Programmabbruch oder die Ausgabe eines Zeichens auf Console, so daß ein Programm wie&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    printf (&amp;quot;Hallo Welt!\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
analoge Resultate bringt wie ein Programm, das nativ auf dem Rechner ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
avrtest ist Open Source, einfach gehalten und auf Geschwindigkeit getrimmt. Die Eingabe von Stimuli ist nicht möglich, und interne Peripherie wird nicht unterstützt.&lt;br /&gt;
&lt;br /&gt;
* Lizenz: GPL v2 oder höher&lt;br /&gt;
* Betriebssysteme: Windows, Linux, etc. Eigentlich keine Einschränkung, da es sich um ein Open-Source Projekt handelt.&lt;br /&gt;
&lt;br /&gt;
=== Weblinks ===&lt;br /&gt;
&lt;br /&gt;
* [http://sourceforge.net/p/winavr/code/HEAD/tree/trunk/avrtest/ avrtest: Projekt und README bei sourceforge]&lt;br /&gt;
* [http://lists.gnu.org/archive/html/avr-gcc-list/2011-06/msg00015.html Beschreibung in der avr-gcc-list Mailingliste (en)]&lt;br /&gt;
&lt;br /&gt;
== BASCOM AVR ==&lt;br /&gt;
&lt;br /&gt;
In [http://www.mcselec.com/ BASCOM AVR] ist ein Simulatorteil enthalten und damit ist auch die Simulation von Geräten (LCD, UART) möglich. BASCOM AVR wird von MCS Electronics verkauft (89€) und ist ein unter Windows laufender [[Basic|BASIC]] [[Compiler]] für Atmel AVR Mikrocontroller. Eine auf 4 KB Codegröße eingeschränkte Demoversion ist kostenlos erhältlich.&lt;br /&gt;
&lt;br /&gt;
== AVR Simulator IDE ==&lt;br /&gt;
&lt;br /&gt;
[http://oshonsoft.com/avr.html AVR Simulator IDE] from oshonsoft.com is a powerful application that supplies AVR developers with user-friendly graphical development environment for Windows with integrated simulator (emulator), Basic compiler, assembler, disassembler and debugger. (Kommerziell, Testversion verfügbar)&lt;br /&gt;
&lt;br /&gt;
== MacSim - AVR simulator for OS X ==&lt;br /&gt;
[http://www.fracturedsoftware.com/macsimavr/ MacSim Alpha 4]&lt;br /&gt;
&lt;br /&gt;
Einschränkungen u.A.:  &amp;quot;There are currently no timers, interrupts, or EEPROM. The processor is &#039;generic&#039; and has fixed size data and instruction memories.&amp;quot;&lt;/div&gt;</summary>
		<author><name>134.106.243.22</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Interrupts&amp;diff=68666</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=68666"/>
		<updated>2012-10-09T08:01:33Z</updated>

		<summary type="html">&lt;p&gt;134.106.243.22: /* Interrupts generell zulassen */&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;
! ISC11 o. ISC01 || ISC10 o. ISC00 ||Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
||Low-Level am Pin löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
||Jede Änderung am Pin löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
||Eine fallende Flanke löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
||Eine steigende Flanke löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&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 SPMRaddr                  ; SPM complete Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
&lt;br /&gt;
.org INT_VECTORS_SIZE&lt;br /&gt;
RESET:                        ; hier beginnt das Hauptprogramm &lt;br /&gt;
&amp;lt;/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>134.106.243.22</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Interrupts&amp;diff=68665</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=68665"/>
		<updated>2012-10-09T08:01:02Z</updated>

		<summary type="html">&lt;p&gt;134.106.243.22: /* Interrupts generell zulassen */&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;
! ISC11 o. ISC01 || ISC10 o. ISC00 ||Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
||Low-Level am Pin löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
||Jede Änderung am Pin löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
||Eine fallende Flanke löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
||Eine steigende Flanke löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&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;sie&#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 SPMRaddr                  ; SPM complete Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
&lt;br /&gt;
.org INT_VECTORS_SIZE&lt;br /&gt;
RESET:                        ; hier beginnt das Hauptprogramm &lt;br /&gt;
&amp;lt;/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>134.106.243.22</name></author>
	</entry>
</feed>