Rotary-Encoder, Teil 2
Wir haben ja schon ausprobiert, wie man einen Rotary-Encoder mit einem
PIC einsetzen kann. Die bisherige Lösung ist aber weder
flexibel
noch optimal. Ich möchte deshalb das Rotary-Encoder-Beispiel etwas
optimieren, um die Vorteile eine Microcontrollers etwas besser
herauszustellen.
Uralt-PICs (wie der 16F84) waren auf externe Bauelemente zur Takterzeugung angewiesen. Modernere PICs haben einen integrierten Taktgenerator, der für viele Anwendungen ausreicht. Der 16F628 besitzt einen internen 4-MHz-Oszillator. Den möchte ich verwenden, um den externen Resonator einzusparen.
Die Ausgabe eines vom Encoder gesteuerten Zählerstandes am
PortB mag nicht für alle Anwendungen geeignet sein. Alternativ
könnte man immer genau eine von 8 LEDs aufleuchten lassen, und
durch Drehen des Encoders die leuchtende LED nach links oder rechts
"verschieben". Wir können mit einem PIC beides realisieren. Man
muss dem PIC aber mitteilen, welche der beiden Anzeigevarianten man
gern hätte. Dies teilt man dem PIC mit Hilfe eines
zusätzlichen Input-Pins mit. Ich verwende dafür das Pin RA2.
Ist es mit Vdd verbunden, dann erfolgt Ausgabe des
8-Bit-Zählwertes am PortB wie bisher auch. Wird aber das Pin RA2
mit Vss verbunden, dann wird immer genau eine LED leuchten, die durch
Drehen des Encoders ausgewählt werden kann.
Vielleicht will man es aber lieber so haben, dass alle LEDs
leuchten, bis auf eine? Das wäre dann also eine inverse Anzeige am
PortB. Auch hier sehe ich beide Möglichkeiten vor, und schalte mit
dem Pin RA3 um. Liegt Pin RA3 auf Vss, dann erfolgt die Anzeige wie
bisher auch. Liegt RA3 aber auf Vdd, dann erfolgt die Anzeige invers.
Einige Encoder rasten mechanisch nur in jeder zweiten Drehstellung.
Das hat zur Folge, dass meine bisherige Schaltung zwischen zwei
Raststellung immer den Zählstand immer um 2 erhöht oder
verringert. Die "leuchtende LED" würde z.B. immer gleich um 2
Positionen verschoben werden. Um auch solche Encoder an der selben
Schaltung betreiben zu können, müsste die Software angepasst
werden. Ich mach die Software einfach flexibel. Sie kann beide
Encoder-Arten korrekt bedienen. Man muss ihr nur mit dem Pin RA4
mitteilen, was für ein Encoder angeschlossen ist. Ein Vdd-Pegel an
RA4 bedeutet, das ein normaler Encoder verwendet wird. Dagegen bedeutet
Vss-Pegel an RA4, das ein Encoder eingesetzt wird, der nur jeweils in
der zweiten Stellung einrastet.
(Hinweis: bei RA2=High und
RA4=Low erfolgt die Anzeige des Zählerstandes nur 7-bittig anstatt
mit 8 Bits. Die LED an RB7 bleibt dann unbenutzt.)
Pin |
Vss-Pegel
(Low) |
Vdd-Pegel
(High) |
RA2 |
Anzeige: 1 von 8 LEDs |
Anzeige: 8-Bit-Zählerstand |
RA3 |
normale Ausgabe |
inverse Ausgabe |
RA4 |
doppelschrittiger Encoder |
normaler Encoder |
Schaltung
Das ist nun die modifizierte Schaltung der
flexiblen Schaltung. Der externe Resonator ist entfallen, und auch auf
einen Resttaster habe ich verzichtet. Die Betriebsart wird nun durch Drahtbrücken oder Jumper
an den Pins RA2 bis RA4 ausgewählt. Die Schaltung ist nun flexibler und einfacher. Für den Aufbau eignet sich eine 18-Pin-Testplatine oder eine 18-Pin-Minitestplatine mit angesteckter LED-Platine. |
Programmablauf
Programmlisting
list p=16f628 ;************************************************************** ;* Pinbelegung ;* ---------------------------------- ;* PORTA: 0 < rotary encoder - B Pin4 ;* 1 < rotary encoder - A Pin5 ;* 2 < H=zaehlen L=schieben ;* 3 < H=invers L=normal ;* 4 < H=einschrittig L=zweischrittig ;* 5 - ;* 6 - ;* 7 - ;* ;* PORTB: 0 > LED ;* 1 > LED ;* 2 > LED ;* 3 > LED ;* 4 > LED ;* 5 > LED ;* 6 > LED ;* 7 > LED ;* ;************************************************************** ; ;sprut (zero) Bredendiek 01/2003 .. 12/2008 ; ; Rotary-Encoder mit LED Zeile ; ; Prozessor 16F628 ; ; Prozessor-Takt 4 MHz intern ; ; Rotary Encoder am PortA 0 & 1 ; LED-Zeile am PortB ; ;************************************************************** ; Includedatei für den 16F628 einbinden #include <P16f628.INC> ERRORLEVEL -302 ; SUPPRESS BANK SELECTION MESSAGES ; Configuration festlegen: ; Power on Timer, kein Watchdog, interner 4-MHz-Oscillator, kein Brown out, kein LV-programming __CONFIG _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT & _BODEN_OFF & _LVP_OFF ; Constanten festlegen #define encoder PORTA #define LED PORTB ; Variable festlegen temp equ 0x20 ; Arbeitsregister counter equ 0x21 ; Zählstand alt equ 0x22 ; alte Rotor-Position neu equ 0x23 ; aktuelle Rotorposition puffer equ 0x24 ; Arbeitsregister ;************************************************************** ; Programmanfang org 0 goto init ;************************************************************** ; Tabelle zur Wandlung von Binär in 1-aus-8-Code ; look up table lut addwf PCL, f retlw B'00000001' ; 0 retlw B'00000010' ; 1 retlw B'00000100' ; 2 retlw B'00001000' ; 3 retlw B'00010000' ; 4 retlw B'00100000' ; 5 retlw B'01000000' ; 6 retlw B'10000000' ; 7 ;************************************************************** ; Initialisierung ********************************************* init bsf STATUS, RP0 ; Bank 1 clrf OPTION_REG movlw B'00000000' ; PortB alle outputs movwf TRISB bcf STATUS, RP0 ; Bank 0 clrf PORTB ; LEDs aus clrf INTCON ; Interupt disable ; 16F628 alle Comparatoreingänge auf Digital umschalten BSF CMCON, CM0 BSF CMCON, CM1 BSF CMCON, CM2 clrf counter movfw encoder movwf alt ; aktuelle encoder-Stellung lesen movlw B'00000011' andwf alt, f ; nur 2 LSB stehen lassen ;Hauptprogrammschleife loop call read_encoder ; Rotary Encoder abfragen movfw counter ; das Zaehlergebnis nach w btfsc PORTA, 4 ; ein- oder zwei-schrittig? goto einschritt ; springen zu einschrittig ; Division des Zaehlerstandes durch 2 movwf puffer bcf STATUS, C rrf puffer, w einschritt btfsc PORTA, 2 ; zaehlen oder schieben? goto binaer ; springen zur Anzeige des 8-Bit-Zaehlergebnisses ; Anzeige einer von 8 LEDs andlw B'00000111' ; auf 0..7 begrenzen call lut ; led-position holen binaer btfss PORTA, 3 ; normal oder invers? goto normal ; springen mormalen Anzeige ; invertieren der Anzeige movwf puffer comf puffer, w normal movwf LED ; Zählerstand anzeigen goto loop ;************************************************************** ; Test des Encoders auf Verdrehung **************************** read_encoder movfw encoder movwf neu ; aktuelle encoder-Stellung nach new movlw B'00000011' andwf neu, f ; nur 2 LSB stehen lassen movfw neu ; wurde der encoder bewegt? movwf temp movfw alt xorwf temp, w ; ist neu = alt? bz ende ; ja: nicht verdreht, also nichts tun ; nein: encoder verdreht, aber in welche Richtung? ; drehen wir mal Bit0 des alten werts zum Vergleich ; nach links bcf alt, 1 clrc ; Carry-Flag löschen rlf alt, f ; alten Encoder-Stand um 1 Stelle nach links drehen movfw neu xorwf alt, f ; falls xorf >1 ergibt, dann war es eine Rechtsdrehung bz links ; links: decrement counter decf alt, f bz links ; links: decrement counter rechts incf counter, f ; rechts: increment counter goto weiter links decf counter, f ; links: decrement counter weiter movfw neu movwf alt ; neu für nächsten Vergleich als alt speichern ende return end |