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
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
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.
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.
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.
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
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 |
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.