Forum: Mikrocontroller und Digitale Elektronik Drehencoder am PIC 16F628


von Bernd P. (bertron)


Lesenswert?

Hallo,

ich möchte einen Drehencoder (mechanisch) mit einem PIC 16F628 
auswerten. Der Encoder hängt am PortA, am PortB hängen 8 LEDs, von denen 
jeweils eine leuchtet.
Erste Versuche habe ich mit einem Code von Sprut gemacht, aber der 
funktioniert leider nicht zuverlässig (einzelne LEDs werden 
übersprungen):
http://www.sprut.de/electronic/pic/programm/rotary2/rotary2.html

Hat jemand vielleicht schonmal den hier mehrfach erwähnten und wohl sehr 
zuverlässigen Code von Peter Dannegger für den PIC 16F628 umgeschrieben?

Bin dankbar für jede Hilfe.

Bernd

von Jens (Gast)


Lesenswert?

Also ich würde mir überlegen, den Encoder vielleicht lieber an die 4 
Lower Bits des PORTB zu hängen, weil du dort einen Interrupt auslösen 
lassen kannst, wenn sich einer der RB0:3 (natürlich als Input 
konfiguriert) ändert. Das könnte das "Übersehen" von Impulsen 
reduzieren.

Aber im Allgemeinen sind Drehencoder nicht sooo nett, die überspringen 
gerne mal einen, daher solltest du das bei deiner Auswertung 
berücksichtigen; siehe dir dazu mal den Wiki-Artikel an: 
http://www.mikrocontroller.net/articles/Drehgeber

Gruß
Jens

von ich (Gast)


Lesenswert?

Ich weiß nicht, ob meine Methode irgendwie verpöhnt ist, ich hab das 
folgender Maßen gemacht:
Diagramm: 
http://www.reichelt.de/index.html?;ACTION=7;LA=3;OPEN=0;INDEX=0;FILENAME=B400%252FSTEC11B.pdf;SID=323ulPNqwQASAAAEo2Sz4fedeedafe8a74e4bffc78de65e77b735 
, Seite 8 oben rechts.

Ich überprüfe nur einen der Beiden Eingänge auf eine steigende Flanke 
(z.B. Leitung A). Wenn im aktuellen Durchlauf der Eingang auf High ist 
und im vorherigem Durchlauf auf Low war (Variable zum speichern), dann 
überprüfe den 2. Eingang (Leitung B). Ist Dieser Low, wurde im UZS 
gedreht und wenn der Eingang High ist, wurde gegen den UZS gedreht. Nach 
einer Flanke muss lediglich x Millisekunden gewartet werden, um ein 
Prellen zu unterdrücken. Dabei könnte theoretisch fast eine halbe 
Periodendauer gewartet werden, was aber ansich unnötig ist.
Vorteil finde ich ist, dass man nur einen Eingang pollen und entprellen 
muss und das die Auswertung ziemlich Simpel ist. Nachteil weiß ich eben 
nicht, bei mir hats bisher sehr gut funktioniert. Man braucht nur einen 
Drehimpulsgeber, der soviele Impulse wie Rastungen hat, z.B. STEC11B09: 
Impulse: 20, Rastungen: 20. Sonst müsste man beide Flanken auswerten.

von ich (Gast)


Lesenswert?

Ach und ..

Jens schrieb:
> Also ich würde mir überlegen, den Encoder vielleicht lieber an die 4
> Lower Bits des PORTB zu hängen...

Wieso 4? Die haben doch nur 3 Anschlüsse und einer davon ist Vdd bzw. 
Vss, oder?

Ich hab mir das immer so Vorgestellt, dass man 2 Kurzhubtaster hat, die 
in einer Platte eingelassen sind und dann rollt man mit einem großem 
Reifen oder großen Rolle einmal von links nach rechts und einmal von 
rechts nach links rüber. Da kann man dann die Richtung bestimmen, die 
Anschlüsse sind gleich und Taster prellen auch usw.

von Grübler (Gast)


Lesenswert?

ich schrieb:
> Ich überprüfe nur einen der Beiden Eingänge auf eine steigende Flanke
> (z.B. Leitung A). Wenn im aktuellen Durchlauf.....

So mach ich das auch. Kontakt A frage ich im Zeittakt
von 1ms ab. 10-20ms ist für normale Tasten besser.
Bei Encodern bekommt man aber bei schnellem Drehen
Schwierigkeiten mit der Auswertung.

von Jens (Gast)


Lesenswert?

ich schrieb:
> Wieso 4? Die haben doch nur 3 Anschlüsse und einer davon ist Vdd bzw.
> Vss, oder?

Es ging um den PORTB, der hat vier untere Bits, und die sind alle im 
Interrupt des PORTB-Wechsels drin, das meinte ich.

Gruß
Jens

von Bernd P. (bertron)


Lesenswert?

Vielen Dank für Eure Antworten.
Leider bin ich blutiger Anfänger in Sachen Assemblerprogrammierung für 
PICs.
Ich habe jetzt den ganzen Vormittag verschiedenes ausprobiert, aber 
alles ohne Erfolg.
Hier nochmal der Code von Sprut - im Prinzip funktioniert der ja, nur 
nicht so zuverlässig. Hat jemand einen Tip, was man da ändern könnte 
oder vielleicht doch nicht einen anderen zuverlässigen ASM-Code für 
diese Aufgabe?
1
        list p=16f628
2
;**************************************************************
3
;*      Pinbelegung
4
;*      ----------------------------------      
5
;*      PORTA:  0 < rotary encoder - B Pin4
6
;*              1 < rotary encoder - A Pin5
7
;*              2 < H=zaehlen      L=schieben
8
;*              3 < H=invers       L=normal
9
;*              4 < H=einschrittig L=zweischrittig
10
;*              5 -
11
;*              6 -
12
;*              7 -
13
;*
14
;*      PORTB:  0 > LED
15
;*              1 > LED
16
;*              2 > LED
17
;*              3 > LED
18
;*              4 > LED
19
;*              5 > LED
20
;*              6 > LED
21
;*              7 > LED
22
;*      
23
;**************************************************************
24
;
25
;sprut (zero) Bredendiek 01/2003 .. 12/2008
26
;
27
; Rotary-Encoder mit LED Zeile
28
;
29
; Prozessor 16F628
30
;
31
; Prozessor-Takt 4 MHz intern
32
;
33
; Rotary Encoder am PortA 0 & 1
34
; LED-Zeile am PortB
35
;
36
;**************************************************************
37
; Includedatei für den 16F628 einbinden
38
39
        #include <P16f628.INC>
40
41
        ERRORLEVEL      -302            ; SUPPRESS BANK SELECTION MESSAGES
42
43
44
; Configuration festlegen:
45
; Power on Timer, kein Watchdog, interner 4-MHz-Oscillator, kein Brown out, kein LV-programming
46
        __CONFIG        _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT & _BODEN_OFF & _LVP_OFF
47
48
; Constanten festlegen
49
#define encoder PORTA
50
#define LED     PORTB
51
52
; Variable festlegen
53
temp    equ     0x20                    ; Arbeitsregister
54
counter equ     0x21                    ; Zählstand
55
alt     equ     0x22                    ; alte Rotor-Position
56
neu     equ     0x23                    ; aktuelle Rotorposition
57
puffer  equ     0x24                    ; Arbeitsregister
58
59
;**************************************************************
60
; Programmanfang
61
62
        org     0
63
        goto    init
64
65
66
;**************************************************************
67
; Tabelle zur Wandlung von Binär in 1-aus-8-Code
68
; look up table
69
lut
70
        addwf PCL, f
71
        retlw B'00000001' ; 0
72
        retlw B'00000010' ; 1
73
        retlw B'00000100' ; 2
74
        retlw B'00001000' ; 3
75
        retlw B'00010000' ; 4
76
        retlw B'00100000' ; 5
77
        retlw B'01000000' ; 6
78
        retlw B'10000000' ; 7
79
80
;**************************************************************
81
; Initialisierung *********************************************
82
83
init
84
        bsf     STATUS, RP0             ; Bank 1
85
        clrf    OPTION_REG
86
        movlw   B'00000000'             ; PortB alle outputs
87
        movwf   TRISB
88
        bcf     STATUS, RP0             ; Bank 0
89
        clrf    PORTB                   ; LEDs aus
90
        clrf    INTCON                  ; Interupt disable
91
92
93
; 16F628 alle Comparatoreingänge auf Digital umschalten
94
        BSF     CMCON, CM0
95
        BSF     CMCON, CM1
96
        BSF     CMCON, CM2
97
98
        clrf    counter
99
        movfw   encoder
100
        movwf   alt                     ; aktuelle encoder-Stellung lesen
101
        movlw   B'00000011'
102
        andwf   alt, f                  ; nur 2 LSB stehen lassen
103
104
;Hauptprogrammschleife
105
loop
106
        call    read_encoder            ; Rotary Encoder abfragen
107
        movfw   counter                 ; das Zaehlergebnis nach w
108
109
        btfsc   PORTA, 4                ; ein- oder zwei-schrittig?
110
        goto    einschritt              ; springen zu einschrittig
111
        ; Division des Zaehlerstandes durch 2
112
        movwf   puffer
113
        bcf     STATUS, C
114
        rrf     puffer, w               
115
einschritt
116
117
        btfsc   PORTA, 2                ; zaehlen oder schieben?
118
        goto    binaer                  ; springen zur Anzeige des 8-Bit-Zaehlergebnisses
119
        ; Anzeige einer von 8 LEDs
120
        andlw   B'00000111'             ; auf 0..7 begrenzen
121
        call    lut                     ; led-position holen
122
binaer
123
124
        btfss   PORTA, 3                ; normal oder invers?
125
        goto    normal                  ; springen mormalen Anzeige
126
        ; invertieren der Anzeige
127
        movwf   puffer
128
        comf    puffer, w               
129
normal
130
        movwf   LED                     ; Zählerstand anzeigen
131
        goto    loop
132
133
134
;**************************************************************
135
; Test des Encoders auf Verdrehung ****************************
136
137
read_encoder
138
        movfw   encoder
139
        movwf   neu                     ; aktuelle encoder-Stellung nach new
140
        movlw   B'00000011'     
141
        andwf   neu, f                  ; nur 2 LSB stehen lassen
142
        movfw   neu                     ; wurde der encoder bewegt?
143
        movwf   temp
144
        movfw   alt
145
        xorwf   temp, w                 ; ist neu = alt?
146
        bz      ende                    ; ja: nicht verdreht, also nichts tun
147
                                        ; nein: encoder verdreht, aber in welche Richtung?
148
                                        ; drehen wir mal Bit0 des alten werts zum Vergleich
149
                                        ;  nach links
150
        bcf     alt, 1
151
        clrc                            ; Carry-Flag löschen
152
        rlf     alt, f                  ; alten Encoder-Stand um 1 Stelle nach links drehen
153
        movfw   neu
154
        xorwf   alt, f                  ; falls xorf >1 ergibt, dann war es eine Rechtsdrehung
155
        bz      links                   ; links:  decrement counter
156
        decf    alt, f
157
        bz      links                   ; links:  decrement counter
158
rechts
159
        incf    counter, f              ; rechts:  increment counter
160
        goto    weiter
161
links
162
        decf    counter, f              ; links:  decrement counter
163
weiter
164
        movfw   neu
165
        movwf   alt                     ; neu für nächsten Vergleich als alt speichern
166
ende
167
        return
168
169
        end

von Jens (Gast)


Lesenswert?

Also ich bin in Assembler wirklich nicht firm, und wenn du es auch nicht 
bist, dann gibt es zwei Möglichkeiten:

1. Du lernst Assembler. Dazu gehört es dann unausweichlich, den 
geposteten Code Befehl für Befehl durchzuackern, und das wird dir 
niemand abnehmen ;-)

2. Du programmierst in C (so mache ich das bis jetzt) wenn es um 
zeitunkritische Sachen geht.

Schaue mal ins Datenblatt des PIC16F628 und suche dir den Interrupt 
raus, der die Pins RB4:7 betrifft. Sorry, habe mich oben vertan, es sind 
nicht die Pins RB0:3. Der Interrupt heißt "PORTB Change Interrupt":

Zum Enablen musst du INTCON<4> setzen (RBIE = 1), und das Interrupt-Flag 
ist dann in INTCON<0>, es heißt RBIF. Denk dran, dass du noch global 
Interrupts enablen musst mit GIE = 1 bzw. gleichbedeutend INTCON<7> = 1.

Wenn du in Assembler programmierst, dann musst du aufs INTCON-Register 
zugreifen, in C kannst du direkt
1
GIE = 1;
2
RBIE = 1;
schreiben.

May the force be with you. ;-)

Gruß
Jens

von Bernd P. (bertron)


Lesenswert?

Hi Jens,

Danke für Deine Hilfestellung. Ja, Du hast recht - Assembler lernen wäre 
das solideste. So ganz habe ich das auch noch nicht aufgegeben...
Aber ich habe die Sache jetzt auch endlich an's Laufen gebracht:
Mein encoder ist nicht zwei-  sondern vierschrittig. Habe jetzt einfach 
mal eine Division durch vier eingebaut und nun klappt es auch 
zufriedenstellend.
Auf den Sprut-Seiten findet sich auch ein weiterer, flexiblerer Code, 
der auch zweischrittige Encode unterstützt. Den habe ich dann einfach um 
die zusätzliche Division erweitert.

Bernd

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.