Inhaltsverzeichnis
Der verwendete Drehencoder
Vor noch gar nicht so langer Zeit war die Auswahl an für den schmalen Bastlergeldbeutel erschwinglichen Drehencodern nicht sehr groß. Der Elektronikversender POLLIN hatte jedoch mit einem Drehencoder vom Hersteller PANASONIC (Datenblatt unter: [ http://www.pollin.de/shop/downloads/D240313D.PDF ] ) ein echtes Schnäppchen für wenige Eurocent im Portfolio.
Diesen Drehencoder gibt es immernoch bei POLLIN, und so ist es nicht weiter verwunderlich, dass er immer noch auf vielen Bastlertischen aufzufinden ist. Vermutlich jeder BASCOM-Anfänger möchte, sobald er eine LED zum Blinken, ein Lauflicht zum Laufen, etc., gebracht hat, auch irgendwann solch einen Drehencoder einsetzten - zumal BASCOM dafür über den ENCODER-Befehl verfügt. Speziell dieser PANASONIC-Encoder, aber natürlich auch die baugleichen Typen, machen hierbei aufgrund ihrer Bauweise (2 Zustandswechsel je Rastung) folgende Probleme:
- Der Encoderwert erhöht/vermindert sich je Rastung um den Wert 2
- Der Encoderwert überspringt schonmal den nächsten Wert
- Der Encoderwert hat Aussetzer
In diesem Artikel möchte ich drei Lösungsansätze zur Auswertung in BASCOM des genannten Encoders und seiner baugleiche Typen aufzeigen.
Bascom ENCODER-Befehl mit Anpassung
Der ENCODER-Befehl braucht eine Anpassung, weil er sämtliche Zustandswechsel der beiden Signaleingänge erfasst. Die Anpassungsroutine wird einfach zyklisch im Hauptprogramm aufgerufen und teilt die Anzahl der Zustandswechsel seit dem letzten Aufruf durch zwei. Das Ergebnis ist die Anzahl der Rastungen in die jeweilige Richtung. Damit ein einzelner Zustandswechsel durch das Schieben nicht unter den Tisch fällt wird dieser vor der Division ausmaskiert und gemerkt. Um Sprünge oder Ausfälle des Encoderwertes zu vermeiden, ist es weiterhin ratsam, den ENCODER-Befehl in festen und definierbaren Zeitabständen auszuführen. Deshalb verwende ich einen Hardware-Timer des AVR und führe den ENCODER-Befehl etwa jede Millisekunde aus. Beim zu schnellen Aufrufen hintereinander des Befehls wirkt sich das Kontaktprellen der mechanischen Encoderkontakte aus, was zu Sprüngen führt. Beim zu langsamen Aufrufen werden nicht alle Zustandswechsel erfasst und tatsächliche Rastungen gehen verloren.
'*******************************************************************************
'*** Testprogramm für POLLIN Dreh-Encoder PANASONIC EVEQDBRL416B ***
'*** Best.Nr. 240 313 ***
'*******************************************************************************
'*** Algorithmus: BASCOM ENCODER-Befehl mit Anpassungsroutine ***
'*******************************************************************************
'*** Author: Screwdriver ***
'*** Datum: 29.08.2009 ***
'*******************************************************************************
'*******************************************************************************
'*** Konfiguration ***
'*******************************************************************************
'AVR
$regfile = "m16def.dat"
$crystal = 8e6
$baud = 9600
'System-Timer für periodische Encoder-Abfrage
Config Timer0 = Timer , Prescale = 256
On Timer0 Isr_timer0
Enable Timer0
Const Timer0_reload = 224 'Wert für 1ms
'*******************************************************************************
'*** Variablen ***
'*******************************************************************************
Dim Rotary0 As Byte 'Phasenzustand des Drehencoders
Dim Rotary1 As Byte 'Zähler der Phasenwechsel
Dim Rotary2 As Byte 'Hilfsvariable
Dim Rotary As Byte 'Encoder-Wert
Dim Rotary_last As Byte 'Letzter Encoder-Wert (nur für Demo)
'*******************************************************************************
'*** Deklaration Unterprogramme ***
'*******************************************************************************
Declare Sub Encoder2() 'Routine zur Anpassung des ENCODER-Befehls
'*******************************************************************************
'*** HAUPTPROGRAMM ***
'*******************************************************************************
Enable Interrupts
Do
Call Encoder2() 'Wird zyklisch im Hauptprogramm aufgerufen
If Rotary_last <> Rotary Then 'Encoder gedreht?
Rotary_last = Rotary 'Neue Stellung merken
Print Rotary 'und Encoder-Wert ausgeben
End If
Loop
End
'*******************************************************************************
'*** ISR_TIMER0 ***
'*** Periodische Auswertung des Dreh-Encoders ***
'*******************************************************************************
Isr_timer0:
Rotary0 = Encoder(pinc.0 , Pinc.1 , Links , Rechts , 0)
Return
'*******************************************************************************
'*** Unterprogramme ***
'*******************************************************************************
Rechts: 'Bei Rechtsdrehung
Rotary1 = Rotary1 + 1 'Zählvariable Phasenwechsel erhöhen
Return
Links: 'Bei Linksdrehung
Rotary1 = Rotary1 - 1 'Zählvariable Phasenwechsel vermindern
Return
'*******************************************************************************
'*** Routine zur Anpassung des ENCODER-Befehls an POLLIN Drehencoder ***
'*******************************************************************************
Sub Encoder2()
Disable Interrupts
Rotary2 = Rotary1 'Zählvariable Phasenwechsel kopieren
Rotary1 = Rotary1 And 1 'Ggf.Phasenwechsel zwischen Rastungen stehenlassen
push r0 'BASCOM kann kein arithmetischen Rechtsschieben
lds r0,{rotary2} ' Aber der AVR kanns!
asr r0 'ASR= Rechtsschieben mit Berücksichtigen
sts {rotary2},r0 'des Vorzeichens, also teilen durch 2
pop r0
Rotary = Rotary + Rotary2 'Encoder-Wert um Anzahl Rastungen erhöhen/vermindern
Enable Interrupts
End Sub
Statemaschine als Alternative zum ENCODER-Befehl
Der BASCOM ENCODER-Befehl ist zwar ebenfalls als Statemaschine ausgeführt, er wertet jedoch nur zwei Zustände, nämlich den aktuellen Zustand und den vorherigen Zustand der Encoder-Pins aus. Durch diese Zustandswechsel erkennt man, dass gedreht wurde und durch Vergleich des aktuellen Zustands mit dem vorherigen lässt sich die Drehrichtung ermitteln. Die Auswertung eines weiteren, dritten Zustands, also dem vorletzten Zustandswechsel, führt zu einer Verminderung der Fehlinterpretation der Zustandswechsel, z.B. durch "Tänzeln" des Encoderknopfes zwischen zwei Rastungen.
'*******************************************************************************
'*** Testprogramm für POLLIN Dreh-Encoder PANASONIC EVEQDBRL416B ***
'*** Best.Nr. 240 313 ***
'*******************************************************************************
'*** Author: Screwdriver ***
'*** Datum: 29.08.2009 ***
'*******************************************************************************
'*** Algorithmus: Statemaschine mit 3 Zuständen ***
'*******************************************************************************
'*******************************************************************************
'*** Konfiguration ***
'*******************************************************************************
'AVR
$regfile = "m16def.dat"
$crystal = 8e6
'System-Timer für periodische Encoder-Abfrage
Config Timer0 = Timer , Prescale = 256
On Timer0 Isr_timer0
Enable Timer0
Const Timer0_reload = 240 'Wert für 0.5ms
'Drehencoder
Config Portc.1 = Input : Set Portc.1 : Ph_a Alias Pinc.1 'Encoder Signal A
Config Portc.0 = Input : Set Portc.0 : Ph_b Alias Pinc.0 'Encoder Signal B
'*******************************************************************************
'*** Variablen ***
'*******************************************************************************
'Variablen
Dim Rotary0 As Byte 'Aktuelles Rasterwert (A,B)
Dim Rotary1 As Byte 'Übergangswert (A,B) zwischen Rastungen
Dim Rotary2 As Byte 'Letzter Rasterwert (A,B)
Dim Rotary As Byte 'Zahlenwert des Encoders
Dim Rotary_last As Byte 'Letzter Encoder-Wert (nur für Demo)
'*******************************************************************************
'*** HAUPTPROGRAMM ***
'*******************************************************************************
Enable Interrupts
Do
If Rotary_last <> Rotary Then 'Encoder gedreht?
Rotary_last = Rotary 'Neue Stellung merken
Print Rotary 'und Encoder-Wert ausgeben
End If
Loop
'*******************************************************************************
'*** ISR_TIMER0 ***
'*** Periodische Auswertung des Dreh-Encoders ***
'*******************************************************************************
Isr_timer0:
Timer0 = Timer0_reload
Shift Rotary0 , Left , 1 'Schieberegister für Entprellung (A,B)
Rotary0.4 = Ph_a 'Phase A lesen -> Bit 4
Rotary0.0 = Ph_b 'Phase B lesen -> Bit 0
If Rotary0 <> Rotary2 Then 'Neues Signalpaar (A,B) ?
Select Case Rotary0
Case &B0000_0000 'Aktuell (A,B)= (0,0)
Select Case Rotary1
Case &B1111_0000 'Übergang war (A,B)= (1,0), also
Incr Rotary 'Encoder-Wert erhöhen
Case &B0000_1111 'Übergang (A,B)= (0,1), also
Decr Rotary 'Encoder-Wert verringern
End Select
Rotary2 = &B0000_0000 'Aktuelles (A,B) in vorheriges (A,B)
Case &B0000_1111 'Übergang (A,B)= (0,1) zwischen Rastungen
Rotary1 = &B0000_1111 'erkennen und in abspeichern
Case &B1111_0000 'Übergang (A,B)= (1,0) zwischen Rastungen
Rotary1 = &B1111_0000 'erkennen und abspeichern
Case &B1111_1111 'Aktuell (A,B)= (1,1)
Select Case Rotary1
Case &B0000_1111 'Übergang war (A,B)= (0,1), also
Incr Rotary 'Encoder-Wert erhöhen
Case &B1111_0000 'Übergang war (A,B)= (1,0), also
Decr Rotary 'Encoder-Wert verringern
End Select
Rotary2 = &B1111_1111 'Aktuelles (A,B) in vorheriges (A,B)
End Select
End If
Return
Danegger'sche Methode als Alternative zum ENCODER-Befehl
Die von mir als Danegger'sche bezeichnete Methode ist die Übersetzung der hocheffizienten C-Routine von Peter Danegger im Mikrocontroller.net [ http://http://www.mikrocontroller.net/articles/Drehgeber ]. Sie wandelt den 2-Bit-Graycode des Encoders in den Binärcode um arithmetische Operationen anwenden zu können. Da sie wie der BASCOM ENCODER-Befehl alle Zustandswechsel erfasst bedarf es ebenfalls einer Anpassungsroutine.
'*******************************************************************************
'*** Testprogramm für POLLIN Dreh-Encoder PANASONIC EVEQDBRL416B ***
'*** Best.Nr. 240 313 ***
'*******************************************************************************
'*** Algorithmus: Danegger'sche Methode ***
'*******************************************************************************
'*** Author: Screwdriver ***
'*** Datum: 29.08.2009 ***
'*******************************************************************************
'*******************************************************************************
'*** Konfiguration ***
'*******************************************************************************
'AVR
$regfile = "m16def.dat"
$crystal = 8e6
'System-Timer für periodische Encoder-Abfrage
Config Timer0 = Timer , Prescale = 64
On Timer0 Isr_timer0
Enable Timer0
Const Timer0_reload = 224 'Wert für 1ms
'Drehencoder
Config Portc.1 = Input : Set Portc.1 : Ph_a Alias Pinc.1 'Encoder Signal A
Config Portc.0 = Input : Set Portc.0 : Ph_b Alias Pinc.0 'Encoder Signal B
'*******************************************************************************
'*** Variablen ***
'*******************************************************************************
Dim Rotary_last As Byte 'Letzter Encoder-Rastung
Dim Rotary_new As Byte 'Aktuelle Encoder-Rastung
Dim Rotary_delta As Byte 'Anzahl an Zustandswechseln
Dim Rotary_diff As Byte 'Zustandsübergänge
Dim Rotary_temp As Byte 'Hilfsvariable
Dim Rotary As Byte 'Zahlenwert des Encoders
Dim Rotary_last_value As Byte 'Letzter Encoder-Wert (nur für Demo)
'*******************************************************************************
'*** Deklaration Unterprogramme ***
'*******************************************************************************
Declare Sub Encoder_init() 'Initialisierungsroutine
Declare Sub Encoder2() 'Anpassung an POLLIN Drehgeber
'*******************************************************************************
'*** HAUPTPROGRAMM ***
'*******************************************************************************
Call Encoder_init()
Enable Interrupts
Do
Call Encoder2()
If Rotary_last_value <> Rotary Then
Rotary_last_value = Rotary
Print Rotary
End If
Loop
End
'*******************************************************************************
'*** ISR_TIMER0 ***
'*** Periodische Auswertung des Dreh-Encoders ***
'*******************************************************************************
Isr_timer0:
Timer0 = Timer0_reload
'Graycode -> Binärcode
Rotary_new = 0
If Ph_a = 1 Then Rotary_new = 3
If Ph_b = 1 Then Rotary_new = Rotary_new Xor 1
Rotary_diff = Rotary_last - Rotary_new 'Differenz Zustandswechsel
'Rotary_delta ist signed byte!
If Rotary_diff.0 = 1 Then 'Wenn ein Zustandswechsel +/-
Rotary_last = Rotary_new 'Anzahl kopieren
Rotary_diff = Rotary_diff And 2 'Vorzeichen-Bit maskieren (Wert = 2)
Rotary_delta = Rotary_delta + Rotary_diff 'Ggf. addieren
Rotary_delta = Rotary_delta - 1 'Korrektur Vorzeichen-Bit
End If
Return
'*******************************************************************************
'*** Unterprogramme ***
'*******************************************************************************
Sub Encoder_init()
Rotary_new = 0
If Ph_a = 1 Then Rotary_new = 3
If Ph_b = 1 Then Rotary_new = Rotary_new Xor 1
Rotary_last = Rotary_new
Rotary_delta = 0
End Sub
Sub Encoder2()
Disable Interrupts
Rotary_temp = Rotary_delta 'Zählvariable Phasenwechsel kopieren
Rotary_delta = Rotary_temp And 1 'Ggf.Phasenwechsel zwischen Rastungen stehenlassen
push r0 'BASCOM kann kein arithmetischen Rechtsschieben
lds r0, {rotary_temp} 'Aber der AVR kanns!
asr r0 'ASR= Rechtsschieben mit Berücksichtigen
sts {rotary_temp},r0 'des Vorzeichens, also teilen durch 2
pop r0
Rotary = Rotary + Rotary_temp 'Encoder-Wert um Anzahl Rastungen erhöhen/vermindern
Enable Interrupts
End Sub
Autor
- screwdriver
