Hi, ich habe einen Drehimpulsgeber ALPS EC11 mit 30 Rastungen und 15 Impulsen - also zwischen 2 Rastungen wechselt jede Spur genau 1x. Jetzt habe ich mir den Code von PeDa genommen und ein wenig angepasst. Dabei bin ich auf das Problem gestoßen, dass der Drehgeber (bzw die Auswertung) Fehler macht, wenn ich die Auswertung zu schnell laufen lasse. Ich hänge mal meinen Code in den Anhang. Wie ihr sehen könnt, ist das mehr oder weniger PeDa's Code zur Drehgeberauswertung, nur ohne Interrupt. Ich habe zu Testzwecken mal auf den Interrupt verzichtet. Am Ende steht ein _delay_ms(1); Damit läuft der Code gut und tut das, was er soll - an PB0 und PB1 hängt jeweils eine LED. Die eine wechselt ihren Zustand bei Drehung nach rechts, die andere bei Drehung nach links. Durch den Delay am Ende der Schleife wird die Auswertung auf 1kHz gedrosselt und wie gesagt, das läuft auch gut. Wenn ich jetzt allerdings den Delay raus nehme, flackern beide LEDs, wenn ich drehe. Zwar ist klar zu erkennen, welche LED zu welcher Drehung gehört, weil die bei jeder Rastung einmal flackert, aber hin und wieder ändert auch die falsche LED ihren Zustand. Diese Fehler passieren aber nur bei schneller Auswertung. Kann mir jemand verraten, warum? Viele Grüße
Nun, die richtige Funktion von Peda gehört in eine Timerinterruptroutine und diese kann bei einige Encoder auch mit 3kHz laufen müssen ! Bei deiner Betrachtung fehlen vollkommen die drei Auswertefunktionen für den Anwender ! Der original Code von Peda funktioniert ! Man muss nur mit der passenden Auswertefunktion die Abfrage im Hauptprogramm machen. Was ist den eine .ino Datei ? C Code sollte man als .c auch speichern.
Da mein Drehencoder 2 Wechsel pro Rastung hat, hab ich die Funktion encode_read2() direkt in die Schleife mit rein gepackt. Die anderen beiden Funktionen brauche ich ja nicht. Die Datei ist aus der Arduino IDE.
drehgeber schrieb: > Kann mir jemand verraten, warum? Weil du das System aushebelst. Der Timer ist ja gerade der Kniff, der den Drehgeber auch bequem im Hintergrund bearbeitet, ohne dabei viel Rechenzeit zu kosten. Mit deinem _delay() legst du den MC ja erstmal lahm, und nach deiner Methode würde es zwar dann gehen. Der Timer aber macht genau diesen 1ms Ticker von alleine, und enthebt dich der Notwendigkeit, das Rad nochmal zu erfinden.
@mschoeldgen: Schon klar, dass der Timer auch diese 1ms warten kann statt dem delay. Meine Frage war, warum es nicht mehr funktioniert, wenn ich zu häufig abfrage... Beispiel: Timer nicht mit 1ms sondern 10us. Die Auswertung macht Fehler. Warum?
drehgeber schrieb: > Wenn ich jetzt allerdings den Delay raus nehme, flackern beide LEDs, > wenn ich drehe. Welchen Sinn soll dieser Kram machen:
1 | val = enc_delta; |
2 | enc_delta = val & 1; |
3 | val = (val >> 1); |
4 | |
5 | if(val > 0) { |
6 | PORTB ^= (1 << PB0); |
7 | val = 0; |
8 | } |
9 | if (val < 0) { |
10 | PORTB ^= (1 << PB1); |
11 | val = 0; |
Schreibe stattdessen einfach
1 | PORTB = enc_delta; // nach DDRB=0xFF; |
und du siehst, daß der Encodercode richtig läuft, dein Kram aber nicht.
:
Bearbeitet durch User
Versuche es einmal damit:
1 | /* Rotary encoder handler for arduino.
|
2 | *
|
3 | * Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3.
|
4 | * Contact: bb@cactii.net
|
5 | *
|
6 | * Quick implementation of rotary encoder routine.
|
7 | *
|
8 | * More info: http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html
|
9 | *
|
10 | */
|
11 | |
12 | // Half-step mode?
|
13 | #define HALF_STEP
|
14 | // Arduino pins the encoder is attached to. Attach the center to ground.
|
15 | #define ROTARY_PIN1 2
|
16 | #define ROTARY_PIN2 3
|
17 | // define to enable weak pullups.
|
18 | #define ENABLE_PULLUPS
|
19 | |
20 | #define DIR_CCW 0x10
|
21 | #define DIR_CW 0x20
|
22 | |
23 | #ifdef HALF_STEP
|
24 | // Use the half-step state table (emits a code at 00 and 11)
|
25 | const unsigned char ttable[6][4] = { |
26 | {0x3 , 0x2, 0x1, 0x0}, {0x23, 0x0, 0x1, 0x0}, |
27 | {0x13, 0x2, 0x0, 0x0}, {0x3 , 0x5, 0x4, 0x0}, |
28 | {0x3 , 0x3, 0x4, 0x10}, {0x3 , 0x5, 0x3, 0x20} |
29 | };
|
30 | #else
|
31 | // Use the full-step state table (emits a code at 00 only)
|
32 | const unsigned char ttable[7][4] = { |
33 | {0x0, 0x2, 0x4, 0x0}, {0x3, 0x0, 0x1, 0x10}, |
34 | {0x3, 0x2, 0x0, 0x0}, {0x3, 0x2, 0x1, 0x0}, |
35 | {0x6, 0x0, 0x4, 0x0}, {0x6, 0x5, 0x0, 0x10}, |
36 | {0x6, 0x5, 0x4, 0x0}, |
37 | };
|
38 | #endif
|
39 | volatile unsigned char state = 0; |
40 | |
41 | /* Call this once in setup(). */
|
42 | void rotary_init() { |
43 | pinMode(ROTARY_PIN1, INPUT); |
44 | pinMode(ROTARY_PIN2, INPUT); |
45 | #ifdef ENABLE_PULLUPS
|
46 | digitalWrite(ROTARY_PIN1, HIGH); |
47 | digitalWrite(ROTARY_PIN2, HIGH); |
48 | #endif
|
49 | }
|
50 | |
51 | /* Read input pins and process for events. Call this either from a
|
52 | * loop or an interrupt (eg pin change or timer).
|
53 | *
|
54 | * Returns 0 on no event, otherwise 0x80 or 0x40 depending on the direction.
|
55 | */
|
56 | unsigned char rotary_process() { |
57 | unsigned char pinstate = (digitalRead(ROTARY_PIN2) << 1) | digitalRead(ROTARY_PIN1); |
58 | state = ttable[state & 0xf][pinstate]; |
59 | return (state & 0x30); |
60 | }
|
61 | |
62 | void setup() { |
63 | Serial.begin(9600); |
64 | rotary_init(); |
65 | }
|
66 | |
67 | void loop() { |
68 | unsigned char result = rotary_process(); |
69 | if (result) { |
70 | Serial.println(result == DIR_CCW ? "LEFT" : "RIGHT"); |
71 | }
|
72 | |
73 | }
|
drehgeber schrieb: > Beispiel: Timer nicht mit 1ms sondern 10us. Die Auswertung macht Fehler. > Warum? Hast du dir denn den Timerinterrupt mal genau angesehen? Der Code fragt den Drehgeber nämlich zweimal ab, bevors einen Schritt weitergeht. Wenn du das zu schnell machst, funktioniert das nicht mehr und der Code vermutet z.B., das der Drehgeber immer hin und her gedreht wird. Und ein normaler mechanischer Drehgeber prellt auch. Die Fehlimpulse sind bei 10µs Zyklus schon im Messbereich der ISR und werden dann auch so ausgewertet - als Fehlimpulse.
Matthias S. schrieb: > Hast du dir denn den Timerinterrupt mal genau angesehen? Der Code fragt > den Drehgeber nämlich zweimal ab, bevors einen Schritt weitergeht. Ja, ich weiß. Daher ja auch der Teil
1 | val = enc_delta; |
2 | enc_delta = val & 1; |
3 | val = (val >> 1); |
Erst, wenn sich enc_delta 2x geändert hat, wird val +/- 1 gesetzt.
drehgeber schrieb: > Kann mir jemand verraten, warum? Der Code zählt dann jeden Preller mit, d.h. man hat scheinbar mehrere Richtungswechsel. Das macht in der Praxis nichts, da ja nach einem Preller wieder sofort zurück gezählt wird. Man hat dann z.B. folgendes Verhalten beim Drehen um einen Schritt: 0-1-2(Preller)-1 Du hebelst das aber elegant aus, indem Du ständig den Wert wieder auf 0 zurück setzt.
@peda: Ich setze den Wert ja nur auf 0 zurück, wenn |enc_delta| >= 2 ist, was "ganzer Schritt" bedeutet...
Marius schrieb: > Siehe Entprellen Drehencoder müssen normalerweise nicht entprellt werden, da die richtige Auswertung nicht über Flankenwechsel, sondern über Zustände gemacht wird(gemacht werden sollte). Man muss natürlich dafür sorgen, das Preller nicht das Auswerteprogramm irritieren oder im Extremfall sogar lähmen.
Harald W. schrieb: > Drehencoder müssen normalerweise nicht entprellt werden, da die > richtige Auswertung nicht über Flankenwechsel, sondern über > Zustände gemacht wird(gemacht werden sollte). Das ist vom Grundsatz her falsch. Drehencoder können prellen oder bei optischen kann die Flanke zu flach sein, sodaß es zu Fehlimpulsen und somit zu Fehlzuständen kommt. Deshalb müssen ALLE Drehencoder geeignet entprellt werden. Entweder analog oder eben digital. Ist im Prinzip wurscht, aber die rein analoge passive per 2 Kondensatoren ist bei weitem die effektivste: extrem billig und ausreichend wirksam und kostet keinerlei Programmieraufwand. Du solltest das mal vom ganz prinzipiellen Blick her verstehen: Ein Programm in einem µC kann einen Port oder eine davon abgeleitete Variable abfragen und das ist ein Samplingvorgang. Also schlägt das Abtast-Theorem zu und es muß bereits VOR der Abtastung dafür gesorgt sein, daß selbiges eingehalten wird, sonst gibt es Alias, was in diesem Falle eben Fehlinterpretation des Drehvorganges bedeutet. W.S.
W.S. schrieb: > Das ist vom Grundsatz her falsch. > Du solltest das mal vom ganz prinzipiellen Blick her verstehen Harald hat völlig recht und du irrst. Sicher ist es eine Auffassungssache, ob eine Auswertung, die keine Probleme und Fehlzählungen durch Kontaktprellen mechanischer Encoder hat, sowieso "entprellt". Aber so lange man keine Flanken sondern Zustände auswertet, wie Harald es richtig erwähnt, interessiert einen überhaupt nicht, ob Kontakte mehrmals zwischen Zuständen hin und her wechseln. Du findest also im richtigen Code keine Befehle zur Entprellung.
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.