Forum: Mikrocontroller und Digitale Elektronik Drehencoder zu schnell auslesen?


von drehgeber (Gast)


Angehängte Dateien:

Lesenswert?

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

von Uwe (de0508)


Lesenswert?

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.

von drehgeber (Gast)


Lesenswert?

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.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

von drehgeber (Gast)


Lesenswert?

@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?

von Michael B. (laberkopp)


Lesenswert?

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
von INO (Gast)


Lesenswert?

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
}

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

von drehgeber (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Marius (Gast)


Lesenswert?

Siehe Entprellen

von drehgeber (Gast)


Lesenswert?

@peda: Ich setze den Wert ja nur auf 0 zurück, wenn |enc_delta| >= 2 
ist, was "ganzer Schritt" bedeutet...

von Harald W. (wilhelms)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Michael B. (laberkopp)


Lesenswert?

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