<?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=88.77.159.107</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=88.77.159.107"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/88.77.159.107"/>
	<updated>2026-04-10T19:45:33Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Tasten&amp;diff=89394</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=89394"/>
		<updated>2015-07-24T13:41:05Z</updated>

		<summary type="html">&lt;p&gt;88.77.159.107: /* Fallbeispiel */&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.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Flankensuche.png|850px]]&lt;br /&gt;
&lt;br /&gt;
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;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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. Die 4 mal sind relativ willkürlich und so gewählt, dass man einen Zähler leicht aufbauen kann. Wird die Interrupt Routine also alle 5 Millisekunden aufgerufen, dann muss die Taste bei 4 Stichproben hintereinander durchgehend gedrückt worden sein. Prellt die Taste in dieser Zeit, dann wird der Zähler einfach auf 0 zurückgesetzt und die Wartezeit beginnt erneut zu laufen. Spätestens 20 Millisekunden nach dem letzten Tastenpreller vermeldet daher diese Routine einen Tastendruck, der dann ausgewertet werden kann.&lt;br /&gt;
&lt;br /&gt;
===Einfache Tastenentprellung und Abfrage===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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>88.77.159.107</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Tasten&amp;diff=89393</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=89393"/>
		<updated>2015-07-24T13:40:50Z</updated>

		<summary type="html">&lt;p&gt;88.77.159.107: syntaxhighlight fehler behoben&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.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Flankensuche.png|850px]]&lt;br /&gt;
&lt;br /&gt;
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;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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. Die 4 mal sind relativ willkürlich und so gewählt, dass man einen Zähler leicht aufbauen kann. Wird die Interrupt Routine also alle 5 Millisekunden aufgerufen, dann muss die Taste bei 4 Stichproben hintereinander durchgehend gedrückt worden sein. Prellt die Taste in dieser Zeit, dann wird der Zähler einfach auf 0 zurückgesetzt und die Wartezeit beginnt erneut zu laufen. Spätestens 20 Millisekunden nach dem letzten Tastenpreller vermeldet daher diese Routine einen Tastendruck, der dann ausgewertet werden kann.&lt;br /&gt;
&lt;br /&gt;
===Einfache Tastenentprellung und Abfrage===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&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;/syntaxhighlight&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>88.77.159.107</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Tasten&amp;diff=89392</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=89392"/>
		<updated>2015-07-24T13:40:37Z</updated>

		<summary type="html">&lt;p&gt;88.77.159.107: syntaxhighlight fehler behoben&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.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Flankensuche.png|850px]]&lt;br /&gt;
&lt;br /&gt;
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;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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. Die 4 mal sind relativ willkürlich und so gewählt, dass man einen Zähler leicht aufbauen kann. Wird die Interrupt Routine also alle 5 Millisekunden aufgerufen, dann muss die Taste bei 4 Stichproben hintereinander durchgehend gedrückt worden sein. Prellt die Taste in dieser Zeit, dann wird der Zähler einfach auf 0 zurückgesetzt und die Wartezeit beginnt erneut zu laufen. Spätestens 20 Millisekunden nach dem letzten Tastenpreller vermeldet daher diese Routine einen Tastendruck, der dann ausgewertet werden kann.&lt;br /&gt;
&lt;br /&gt;
===Einfache Tastenentprellung und Abfrage===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&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;/syntaxhighlight&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>88.77.159.107</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Tasten&amp;diff=89391</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=89391"/>
		<updated>2015-07-24T13:40:25Z</updated>

		<summary type="html">&lt;p&gt;88.77.159.107: syntaxhighlight fehler behoben&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.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Flankensuche.png|850px]]&lt;br /&gt;
&lt;br /&gt;
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;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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. Die 4 mal sind relativ willkürlich und so gewählt, dass man einen Zähler leicht aufbauen kann. Wird die Interrupt Routine also alle 5 Millisekunden aufgerufen, dann muss die Taste bei 4 Stichproben hintereinander durchgehend gedrückt worden sein. Prellt die Taste in dieser Zeit, dann wird der Zähler einfach auf 0 zurückgesetzt und die Wartezeit beginnt erneut zu laufen. Spätestens 20 Millisekunden nach dem letzten Tastenpreller vermeldet daher diese Routine einen Tastendruck, der dann ausgewertet werden kann.&lt;br /&gt;
&lt;br /&gt;
===Einfache Tastenentprellung und Abfrage===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&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;/syntaxhighlight&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>88.77.159.107</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Tasten&amp;diff=89390</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=89390"/>
		<updated>2015-07-24T13:39:26Z</updated>

		<summary type="html">&lt;p&gt;88.77.159.107: syntaxhighlight fehler behoben&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.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Flankensuche.png|850px]]&lt;br /&gt;
&lt;br /&gt;
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;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def 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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&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;/syntaxhighlight&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. Die 4 mal sind relativ willkürlich und so gewählt, dass man einen Zähler leicht aufbauen kann. Wird die Interrupt Routine also alle 5 Millisekunden aufgerufen, dann muss die Taste bei 4 Stichproben hintereinander durchgehend gedrückt worden sein. Prellt die Taste in dieser Zeit, dann wird der Zähler einfach auf 0 zurückgesetzt und die Wartezeit beginnt erneut zu laufen. Spätestens 20 Millisekunden nach dem letzten Tastenpreller vermeldet daher diese Routine einen Tastendruck, der dann ausgewertet werden kann.&lt;br /&gt;
&lt;br /&gt;
===Einfache Tastenentprellung und Abfrage===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&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;/syntaxhighlight&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;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&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;/syntaxhighlight&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>88.77.159.107</name></author>
	</entry>
</feed>