Flankenerkennung

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

Taste macht Druck

Häufig möchte man einen Tastendruck auswerten, der genau einmal eine bestimmte Aktion durchführen soll, wie das Senden eines Textes an ein Display. Fragt man den Port des µC auf traditionelle Weise ab und verzweigt je nach Zustand in einen bestimmten Programmteil, kann man so auf einen Tastendruck reagieren. In jedem Zyklus wird das Eingangsbit abgefragt und entsprechend verzweigt.
Das ist solange in Ordnung, wie man beispielsweise ein LED einschalten möchte, die solange leuchtet, wie man der Taster gedrückt hält. Will man aber die LED bei einem Druck ein-, beim Nächsten wieder ausschalten, dann würde die LED mit jedem Zyklus den Zustand wechseln, sprich blinken.
Meist blinkt sie auf Grund des Prozessortaktes so schnell, dass man nur ein Leuchten wahrnimmt, was geringfügig dunkler ist als das der "ungetakteten" LED.

Kurze Flanke

Also was tun? Die Lösung lautet: Flankenauswertung. Eine Flanke ensteht immer dann, wenn ein Signal seinen Zustand ändert, zum Beispiel von High nach Low. Wir sprechen dann sinnfällig von einer fallenden Flanke, das Signal geht nach unten. Umgekehrt ist es also eine steigende Flanke, das Signal kommt aus dem Low-Zustand auf High.
Wenn man also ein Rezept hätte, um festzustellen, ob die Taste gedrückt ist und ob sie es im Zyklus vorher auch schon war? Die Zutaten sind einfach. Alles was dazu benötigt wird, ist ein Speicher, der den Tastenzustand über den Zyklus hinaus bereithält, um ihn mit dem Zustand der Taste im Folgezyklus zu vergleichen. Die Flanke soll wahrhaft kurz sein, nämlich nur einen Zyklus lang.

Hardware

Prinzipiel gilt das im folgende Gesagte für alle beliebigen Rechnewerke, der Autor benutzt den Code der AVR-Prozessoren. Die Befehle lauten bei vielen Prozessoren gleich oder ähnlich, wenn auch die Notation "Befehl Ziel, Quelle" bei anderen Syntax vertauscht sind, nämlich "Befehl Quelle, Ziel".
Alle, die mal einen Intel Prozessor in Assembler programmiert haben und danach mit einem Motorola in Berührung kamen, wissen, dass man immer wieder aufpassen muss, wenn man die Arbeitsplattform wechselt.

Software

Dazu benötigt man zwar ganze drei Register. Eines speichert den Zustand der Eingänge, ein weiteres Registerpaar wertet die Dynamik der Signale aus. Dafür kann man aber gleich eine ganzen Port "abflanken" Was die Register tun, soll vereinfacht beschrieben werden:
Man nehme drei Register E1, M1 und M2...

Ein Signal mit Vergangenheit

M2 ist zu Beginn der Programmbearbeitung jungfräulich, hat also keine Vorgeschichte. Das Programm fragt nun ab: "Eingang E1, bist du aktiv? Und du, M2, weißt du ob Eingang E1 im letzten Zyklus aktiv war?" Wenn E1 bejaht und M2 verneint, dann ist E1 also zum ersten Mal aktiv geworden. Das Programm sagt nun: "M1, merke dir das und sage dem Unterprogramm, wir haben eine steigende Flanke! Und du M2: Merke dir den Zustand von E1 für den nächsten Zyklus!" In logischen Verknüpfungen sieht das so aus:
Hinweis: Die Negierung wird durch das vorangestellte "/" ausgedrückt.
A1) M1=E1 AND /M2
A2) M2=E1
Wichtig ist dabei, dass man sich vor Augen hält, dass die beiden Aussagen nacheinander geprüft werden, so dass das Ergebnis A2 die Aussage A1 während des selben Zyklus nicht beeinflusst.
Ebenso verhält sich das beim "Zyklus-Flip-Flop", welches man mit der Abfrage:
M3=/M3 realisieren kann - der Zustand von M3 ändert sich mit jedem Zyklus. Dass kann oft praktisch sein, wenn man auf einen freien Pin eine Recheckspannung erzeugen will, um a) zu sehen ob das Programm noch läuft und b) um Beispielsweise die Zeitschleife mit dem Oszilloskop zu prüfen.

Das führt dazu, dass sich M2 im folgenden Durchlauf erinnert, wie E1 beim letzten Mal geschaltet war und antworten kann: "Ja, E1 war eben schon geschaltet, du musst nichts mehr tun". Sprich M1 ist dann logisch Null.

Codefragment

.equ S1=0
.equ LED1=1
.def E1=R0
.def M1=R1
.def M2=R2


EdgeLoop:
;***** Erkennen der Flanke *****
      in    E1, pind
      neg   M2        ; Vorbereiten für logischen Vergleich
      and   M2, pind  ; M1 = E1 AND NOT M2 
      mov   M1, M2

;den aktuellen Zustand von E1 speichern
      mov   M2, E1    ; M2 = E1

;***** Verwenden der Flanke *****
      sbrs M1, S1
      rjmp L1          ;nein, M1 nicht gesetzt
      sbis portd, LED1 ;wenn Flanke erkannt, Zustand der LED abfragen
      rjmp L2          ;wenn die LED eingeschaltet ist, springen
      cbi portd, LED1  ;wenn nicht eingeschaltet, dann ausschalten
      rjmp L3          ;nächste Zeile überspringen, sonst geht die LED gleich 
                       ;wieder an
L2:   sbi portd, LED1  ; hierher wird versprungen, wenn LED aus war

      rjmp EdgeLoop

Die eigentliche Flankenerkennung besteht also nur aus fünf Zeilen.
Um eine fallende Flange abzufragen, muss man nur eine einzige Codezeile mehr verwenden:

;***** Erkennen der Flanke *****
      in    E1, pind
      neg   E1        ; vorbereiten für logischen Vergleich 
      neg   M2        ; vorbereiten für logischen Vergleich
      and   M2, pind  ; M1 = E1 AND NOT M2 
      mov   M1, M2

;den aktuellen Zustand von E1 speichern
      mov   M2, E1    ; M2 = E1

Hier schaltet die LED erst um, wenn man den Taster wieder los lässt. Der Vorteil dieser Methode ist, dass man gleich mehrere Tasten gleichzeitig überwachen kann, wenn sie am gleichen Register angeschlossen sind.

Letzter Schliff

Beim ersten Durchlauf nach dem Reset ist M2 stets Null. Fragt man die fallende Flanke ab, passiert nun folgendes:
E1=0, M2=0
/E1=1, /M2=1
==> M1 = 1 AND 1 = 1
Gleich zu Beginn wird die LED also eingeschaltet. Um dies zu verhindern, fügt man eine Initialisierung in den Programmabschnitt ein, der nur einmal nach dem Reset abgearbeitet wird:

Init:
     SER M2 ;M1 muss nicht explizit gelöscht werden,
            ;wenn die Flankenauswertung vor der ersten 
            ;Abfrage von M1 erfolgt
     
Main: ;******** Hauptprogramm **********

Damit schafft man eine Anfangsbedingung, die dem Prozessor "vorflunkert":
"Die Taste war schon im vorherigen Zyklus auf Null." Somit bleibt die LED nach dem Reset aus.
Selbstverständlich kann man mit weiteren Registern die beiden Flanken getrennt abfragen und auf fallende und steigende Flanken zu reagieren. --Mfriedl 23:04, 8. Sep. 2008 (CEST)