K (→Danegger'sche Methode als Alternative zum ENCODER-Befehl) |
K (→Statemaschine als Alternative zum ENCODER-Befehl) |
||
Zeile 113: | Zeile 113: | ||
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. | 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. | + | Die Auswertung eines weiteren, dritten Zustands, also dem vorletzten Zustandswechsel, führt zu einer Verminderung der Fehlinterpretation der Zustandswechsel, verursacht z.B. durch "Tänzeln" des Encoderknopfes zwischen zwei Rastungen. |
<pre> | <pre> |
Version vom 29. August 2009, 15:47 Uhr
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 immer noch 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 baugleichen 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, verursacht 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 = 256 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 und Rotary_diff sind signed byte! If Rotary_diff.0 = 1 Then 'Wenn ein Zustandswechsel +/- Rotary_last = Rotary_new 'Neuen Zustand merken 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