Aus RN-Wissen.de
Wechseln zu: Navigation, Suche


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.

PANASONIC EVEQDBRL416B

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