Das Thema Drehgeber ließ mir nun doch keine Ruhe mehr. Also habe ich einen von diesen (Billig-)Drehgeber von Pollin (Best.Nr. 240 313) ausgekramt und an mein AVR-Bastelboard nach angehängtem Plan angeschlossen.
R1 ist lediglich ein Schutzwiderstand, falls einer der Eingänge PD4..PD6 versehentlich doch mal als Ausgang konfiguriert ist. Es befinden sich bis auf die aktivierten internen PullUp-Widerstände des AVR keine weiteren Bauteile (zwecks Entprellung) im Signalweg des Dreh-Encoders.
Wie ich schon im vorigen Post schrieb, setze ich diesen Drehgebertyp in einem Projekt in der von dort beschriebenen Weise mittels externem Interrupt ein. Das Prinzip und auch der Nachteil dieser Lösung ist, dass jeder Signalwechsel am Interrupteingang die zugehörige Interrupt-Service-Routine (ISR) ausführen lässt. Was heute noch scheinbar einwandfrei funktioniert, kann bei einem ausgelutschtem Drehgeber mit ggf. starkem Kontaktprellen zu einer unkontrollierbaren Prozessorlast führen. Aus diesem Grund werden in Expertenkreisen generell Kontakte mittels einer Timer-Interrupt gesteuerten Routine entprellt.
Die Bascom-eigene Lösung mit dem Encoder-Befehl arbeitete bei mir noch nie zufriedenstellend - egal ob in der Hauptprogrammschleife oder per Timerinterrupt ausgeführt - traten immer Sprünge oder Impulsverluste am Drehgeber auf. Daraufhin habe ich die untenstehende Lösung ausgearbeitet. Beim Testen konnte ich bisher absolut keine Fehler erkennen. Kritik und Verbesserungsvorschläge sind gerne willkommen.
Lösungsweg:
Zur Entprellung werden die beiden Drehgebersignalpaar (A,B) alle 0.5ms in der Timer0-ISR abgefragt und im Schieberegister-Byte ROTARY im Format AAAABBBB abgelegt. Nur wenn alle A's und alle B's gleich sind, also (0000_0000), (0000_1111), (1111_1111) oder (1111_0000) gilt das Encodersignalpaar als stabil.
Außer dem Kontaktprellen besitzt dieser Drehgebertyp aber noch ein weiteres zu beseitigendes Übel:
Grundsätzlich stehen in den Rastungen des Drehknopfes die Signalpaarungen (A,B)=(0,0) bzw. (A,B)=(1,1) stabil an. Einige der 16 Rastungen sind aber so unpräzise, das schon durch bloßes Anfassen des Drehknopfes ein Signalwechsel zu (A,B)=(1,0) bzw. (A,B)=(0,1) und beim Loslassen wieder zurück nach (A,B)=(0,0) bzw. (A,B)=(1,1) erfolgt. Dies wird in vielen Routinen (so auch in meiner vom Externen Interrupt gesteuerten Routine) fälschlicherweise als Drehbewegung des Knopfes interpretiert.
Um diesen Fehler zu beheben muß eine Encoderauswertung drei aufeinanderfolgende Signalpaarungen berücksichtigen. Bei mir geschieht das mittels den Variablen
ROTARY: Signalpaarung (A,B) in der aktuellen Rastung
ROTARY1: (A,B) im Übergang zwischen zwei Rastungen (Drehrichtungserkennung)
ROTARY2: Signalpaarung (A,B) in der vorherigen Rastung
Befindet sich der Drehknopf in der Rastung mit (A,B)=(0,0) bzw. (A,B)=(1,1) so muß bei einer Drehung um eine Rastung anschliessend genau die entgegengesetzte Signalpaarung vorliegen, nämlich (A,B)=(1,1) bzw. (A,B)=(0,0). D.h. eine Drehbewegung kann nur stattgefunden haben wenn ROTARY ungleich ROTARY2 ist. Abhängig von der Signalpaarung ROTARY1 beim Übergang zwischen den Rastungen (A,B)=(0,1) bzw. (A,B)=(1,0) wird die Drehrichtung erkannt.
Zur Auswertung des Encoders werden nach vollzogener Rastung entsprechend der Drehrichtung die Flags R_Flag bei Rechtsdrehung und L_Flag bei Linksdrehung gesetzt und der Encoderwert ROTARY_VALUE bei Rechtsdrehung inkrementiert bzw. bei Linksdrehung dekrementiert und können in der Hauptprogrammschleife abgefragt und zurückgesetzt werden.
Code:'******************************************************************************* '*** Testprogramm für POLLIN Dreh-Encoder PANASONIC EVEQDBRL416B *** '*** Best.Nr. 240 313 *** '******************************************************************************* '*** Author: Screwdriver *** '*** Datum: 28.06.2009 *** '******************************************************************************* '******************************************************************************* '*** Konfiguration *** '******************************************************************************* 'AVR $regfile = "m644def.dat" $crystal = 20e6 'System-Timer für periodische Encoder-Abfrage Config Timer0 = Timer , Prescale = 256 On Timer0 Isr_timer0 Enable Timer0 Const Timer0_reload = 217 'Wert für 0.5ms 'Drehencoder Config Portd.4 = Input : Set Portd.4 : Rotary_button Alias Pind.4 'Taste Config Portd.5 = Input : Set Portd.5 : Ph_a Alias Pind.5 'Encoder Signal A Config Portd.6 = Input : Set Portd.6 : Ph_b Alias Pind.6 'Encoder Signal B '******************************************************************************* '*** Variablen *** '******************************************************************************* 'Variablen Dim Rotary As Byte 'Aktuelles Rasterwert (A,B) Dim Rotary1 As Byte 'Übergangswert (A,B) zwischen Rastungen Dim Rotary2 As Byte 'Letzter Rasterwert (A,B) Dim R_flag As Bit , L_flag As Bit 'Flags für Links- / Rechtsdrehung Dim Rotary_value As Byte 'Zahlenwert des Encoders Dim Rotary_key As Byte : Rotary_key = &HFF 'Schieberegister Taste, &HFF: nicht gedrückt Dim Press_flag As Bit 'Flag Taste wurde gedrückt Dim Release_flag As Bit 'Flag Taste wurde losgelassen '******************************************************************************* '*** HAUPTPROGRAMM *** '******************************************************************************* Enable Interrupts Do 'Beispiel zur Auswertung der Flags und des Encoder-Werts im Hauptprogramm If R_flag = 1 Then 'Drehung nach Rechts Reset R_flag Print "Rechts" Print Rotary_value End If If L_flag = 1 Then 'Drehung nach Links Reset L_flag Print "Links" Print Rotary_value End If If Press_flag = 1 Then 'Taste wurde gedrückt Reset Press_flag Print "gedrueckt" End If If Release_flag = 1 Then 'Taste wurde losgelassen Reset Release_flag Print "losgelassen" End If Loop End '******************************************************************************* '*** ISR_TIMER0 *** '*** Periodische Auswertung des Dreh-Encoders *** '******************************************************************************* Isr_timer0: Timer0 = Timer0_reload Shift Rotary , Left , 1 'Schieberegister für Entprellung (A,B) Rotary.4 = Ph_a 'Phase A lesen -> Bit 4 Rotary.0 = Ph_b 'Phase B lesen -> Bit 0 If Rotary <> Rotary2 Then 'Neues Signalpaar (A,B) ? Select Case Rotary Case &B0000_0000 'Aktuell (A,B)= (0,0) If Rotary1 = &B1111_0000 Then 'Übergang war (A,B)= (1,0), also Set R_flag 'Rechtsdrehung Incr Rotary_value 'Encoder-Wert erhöhen Elseif Rotary1 = &B0000_1111 Then 'Übergang (A,B)= (0,1), also Set L_flag 'Linksdrehung Decr Rotary_value 'Encoder-Wert verringern End If 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) If Rotary1 = &B0000_1111 Then 'Übergang war (A,B)= (0,1), also Set R_flag 'Rechtsdrehung Incr Rotary_value 'Encoder-Wert erhöhen Elseif Rotary1 = &B1111_0000 Then 'Übergang war (A,B)= (1,0), also Set L_flag 'Linksdrehung Decr Rotary_value 'Encoder-Wert verringern End If Rotary2 = &B1111_1111 'Aktuelles (A,B) in vorheriges (A,B) End Select End If Shift Rotary_key , Left , 1 'Schieberegister für Entprellung Taste Encoder Rotary_key.0 = Rotary_button 'Taste lesen -> Bit 0 If Rotary_key = &B1000_0000 Then 'Taste wurde gedrückt Set Press_flag Elseif Rotary_key = &B0111_1111 Then 'Taste wurde losgelassen Set Release_flag End If Return







Zitieren
Lesezeichen