K |
(→Dannegger'sche Methode als Alternative zum ENCODER-Befehl) |
||
(Eine dazwischenliegende Version von einem anderen Benutzer wird nicht angezeigt) | |||
Zeile 209: | Zeile 209: | ||
==Dannegger'sche Methode als Alternative zum ENCODER-Befehl== | ==Dannegger'sche Methode als Alternative zum ENCODER-Befehl== | ||
− | Die von mir als | + | Die von mir als Dannegger'sche bezeichnete Methode ist die Übersetzung der hocheffizienten C-Routine von Peter Dannegger im Mikrocontroller.net [ 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 | arithmetische Operationen anwenden zu können. Da sie wie der BASCOM ENCODER-Befehl alle Zustandswechsel | ||
erfasst bedarf es ebenfalls einer Anpassungsroutine. | erfasst bedarf es ebenfalls einer Anpassungsroutine. | ||
Zeile 320: | Zeile 320: | ||
Enable Interrupts | Enable Interrupts | ||
End Sub | End Sub | ||
+ | </pre> | ||
+ | |||
+ | ==Hannes-Lux-Methode als Alternative zum ENCODER-Befehl== | ||
+ | Der Assembler-Programmierer Hannes Lux hat hier [http://www.mikrocontroller.net/topic/172127#new www.mikrocontroller.net] sein Prinzip der Auswertung des Panasonic-Encoders veröffentlicht. Deshalb nenne ich sie hier die Hannes-Lux-Methode. | ||
+ | Er liest die aktuellen Signalzustände des Encoders ein und generiert mit den Signalzuständen der vorherigen Abfrage eine 4-Bit-Sprungtabelle. Die Sprungziele enthalten die Encoderinkremente, welche einfach auf den Encoderwert aufaddiert werden. Ein Encoderinkrement von 255 entspricht -1. Wurde nicht am Encoder gedreht oder es liegt eine ungültige Signalfolge vor, enthält das Sprungziel das Encoderinkrement 0. | ||
+ | |||
+ | <pre> | ||
+ | '******************************************************************************* | ||
+ | '*** Testprogramm für POLLIN Dreh-Encoder PANASONIC EVEQDBRL416B *** | ||
+ | '*** Best.Nr. 240 313 *** | ||
+ | '******************************************************************************* | ||
+ | '*** Algorithmus: Hannes-Lux-Methode *** | ||
+ | '******************************************************************************* | ||
+ | '*** Author: Screwdriver *** | ||
+ | '*** Datum: 28.03.2010 *** | ||
+ | '******************************************************************************* | ||
+ | |||
+ | '******************************************************************************* | ||
+ | '*** 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 As Byte 'Signalzustände Encoder | ||
+ | Dim Rotary_diff As Byte 'Differenz Encoder Rastungen +/- | ||
+ | Dim Rotary_value As Byte 'Encoder Zählwert | ||
+ | |||
+ | Dim Rotary_last_value As Byte 'Letzter Encoder Zählwert (nur für Demo) | ||
+ | |||
+ | |||
+ | '******************************************************************************* | ||
+ | '*** HAUPTPROGRAMM *** | ||
+ | '******************************************************************************* | ||
+ | Enable Interrupts | ||
+ | |||
+ | Do | ||
+ | If Rotary_last_value <> Rotary_value Then | ||
+ | Rotary_last_value = Rotary_value | ||
+ | Print Rotary_value | ||
+ | End If | ||
+ | Loop | ||
+ | |||
+ | '******************************************************************************* | ||
+ | '*** ISR_TIMER0 *** | ||
+ | '*** Periodische Auswertung des Dreh-Encoders *** | ||
+ | '******************************************************************************* | ||
+ | Isr_timer0: | ||
+ | Timer0 = Timer0_reload | ||
+ | |||
+ | Rotary = Rotary * 4 'Aktuelle Pin einlesen | ||
+ | Rotary.1 = Ph_a | ||
+ | Rotary.0 = Ph_b | ||
+ | Rotary = Rotary And &B0000_1111 | ||
+ | |||
+ | Rotary_diff = Lookup(rotary , Rotary_table) | ||
+ | Rotary_value = Rotary_value + Rotary_diff | ||
+ | |||
+ | Return | ||
+ | |||
+ | |||
+ | '******************************************************************************* | ||
+ | '*** Sprungtabelle Encoder *** | ||
+ | '******************************************************************************* | ||
+ | Rotary_table: | ||
+ | Data 0 , 0 , 1 , 0 , 0 , 0 , 0 , 255 , 255 , 0 , 0 , 0 , 0 , 1 , 0 , 0 | ||
+ | |||
</pre> | </pre> | ||
Aktuelle Version vom 28. März 2010, 11:49 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 einsetzen - 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 schon mal 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 resultierende 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: Timer0 = Timer0_reload 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 arithmetisches 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 'eines signed byte 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
Dannegger'sche Methode als Alternative zum ENCODER-Befehl
Die von mir als Dannegger'sche bezeichnete Methode ist die Übersetzung der hocheffizienten C-Routine von Peter Dannegger im Mikrocontroller.net [ 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: Dannegger'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 arithmetisches 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 'eines signed byte Rotary = Rotary + Rotary_temp 'Encoder-Wert um Anzahl Rastungen erhöhen/vermindern Enable Interrupts End Sub
Hannes-Lux-Methode als Alternative zum ENCODER-Befehl
Der Assembler-Programmierer Hannes Lux hat hier www.mikrocontroller.net sein Prinzip der Auswertung des Panasonic-Encoders veröffentlicht. Deshalb nenne ich sie hier die Hannes-Lux-Methode. Er liest die aktuellen Signalzustände des Encoders ein und generiert mit den Signalzuständen der vorherigen Abfrage eine 4-Bit-Sprungtabelle. Die Sprungziele enthalten die Encoderinkremente, welche einfach auf den Encoderwert aufaddiert werden. Ein Encoderinkrement von 255 entspricht -1. Wurde nicht am Encoder gedreht oder es liegt eine ungültige Signalfolge vor, enthält das Sprungziel das Encoderinkrement 0.
'******************************************************************************* '*** Testprogramm für POLLIN Dreh-Encoder PANASONIC EVEQDBRL416B *** '*** Best.Nr. 240 313 *** '******************************************************************************* '*** Algorithmus: Hannes-Lux-Methode *** '******************************************************************************* '*** Author: Screwdriver *** '*** Datum: 28.03.2010 *** '******************************************************************************* '******************************************************************************* '*** 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 As Byte 'Signalzustände Encoder Dim Rotary_diff As Byte 'Differenz Encoder Rastungen +/- Dim Rotary_value As Byte 'Encoder Zählwert Dim Rotary_last_value As Byte 'Letzter Encoder Zählwert (nur für Demo) '******************************************************************************* '*** HAUPTPROGRAMM *** '******************************************************************************* Enable Interrupts Do If Rotary_last_value <> Rotary_value Then Rotary_last_value = Rotary_value Print Rotary_value End If Loop '******************************************************************************* '*** ISR_TIMER0 *** '*** Periodische Auswertung des Dreh-Encoders *** '******************************************************************************* Isr_timer0: Timer0 = Timer0_reload Rotary = Rotary * 4 'Aktuelle Pin einlesen Rotary.1 = Ph_a Rotary.0 = Ph_b Rotary = Rotary And &B0000_1111 Rotary_diff = Lookup(rotary , Rotary_table) Rotary_value = Rotary_value + Rotary_diff Return '******************************************************************************* '*** Sprungtabelle Encoder *** '******************************************************************************* Rotary_table: Data 0 , 0 , 1 , 0 , 0 , 0 , 0 , 255 , 255 , 0 , 0 , 0 , 0 , 1 , 0 , 0
Autor
- screwdriver