Forum: Mikrocontroller und Digitale Elektronik Drehencoder zählt doppelt.


von Jan H. (janiiix3)


Lesenswert?

nabend,

habe mir gerade mal den Quellcode angeschaut:
1
/************************************************************************/
2
/*                                                                      */
3
/*            Drehgeber mit wackeligem Rastpunkt dekodieren             */
4
/*                                                                      */
5
/************************************************************************/
6
 
7
#include <avr/io.h>
8
#include <avr/interrupt.h>
9
#include <avr/pgmspace.h>
10
 
11
#define XTAL        8e6                 // 8MHz
12
 
13
#define PHASE_A     (PINA & 1<<PA1)     // an Pinbelegung anpassen
14
#define PHASE_B     (PINA & 1<<PA3)     // an Pinbelegung anpassen
15
 
16
#define LEDS_DDR    DDRC
17
#define LEDS        PORTC               // LEDs gegen Vcc geschaltet
18
 
19
volatile int8_t enc_delta;              // Drehgeberbewegung zwischen
20
                                        // zwei Auslesungen im Hauptprogramm
21
 
22
// Dekodertabelle für wackeligen Rastpunkt
23
// halbe Auflösung
24
const int8_t table[16] PROGMEM = {0,0,-1,0,0,0,0,1,1,0,0,0,0,-1,0,0};    
25
 
26
// Dekodertabelle für normale Drehgeber
27
// volle Auflösung
28
//const int8_t table[16] PROGMEM = {0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0};    
29
 
30
ISR( TIMER0_COMP_vect )             // 1ms fuer manuelle Eingabe
31
{
32
    static int8_t last=0;           // alten Wert speichern
33
 
34
    last = (last << 2)  & 0x0F;
35
    if (PHASE_A) last |=2;
36
    if (PHASE_B) last |=1;
37
    enc_delta += pgm_read_byte(&table[last]);
38
}
39
 
40
void encode_init( void )            // nur Timer 0 initialisieren
41
{
42
  TCCR0 = (1<<WGM01) | (1<<CS01) | (1<<CS00);     // CTC, XTAL / 64
43
  OCR0 = (uint8_t)(XTAL / 64.0 * 1e-3 - 0.5);       // 1ms
44
  TIMSK |= 1<<OCIE0;
45
}
46
 
47
int8_t encode_read( void )         // Encoder auslesen
48
{
49
  int8_t val;
50
 
51
  // atomarer Variablenzugriff  
52
  cli();
53
  val = enc_delta;
54
  enc_delta = 0;
55
  sei();
56
  return val;
57
}
58
 
59
int main( void )
60
{
61
  int32_t val = 0;
62
 
63
  LEDS_DDR = 0xFF;
64
  encode_init();
65
  sei();
66
 
67
  while(1){
68
    val += encode_read();      
69
    LEDS = val;
70
  }
71
}

Und muss feststellen das mein Encoder zwei Impulse liefert bis er 
einrastet.

Wie kann ich dies am besten umgehen?

von Peter C. (peter_c49)


Lesenswert?

Hallo,
>Wie kann ich dies am besten umgehen?
val += encode_read() / 2;

mfG
Peter

von wendelsberg (Gast)


Lesenswert?

Jan H. schrieb:
> Und muss feststellen das mein Encoder zwei Impulse liefert bis er
> einrastet.
>
> Wie kann ich dies am besten umgehen?

Immer genau 2 oder nur manchmal?

Entprellung ist das Stichwort, nach dem Du suchst.

wendelsberg

von Falk B. (falk)


Lesenswert?

So.
1
int8_t encode_read2( void )         // read two step encoders
2
{
3
  int8_t val;
4
 
5
  cli();
6
  val = enc_delta;
7
  enc_delta = val & 1;
8
  sei();
9
  return val >> 1;
10
}

von Leo B. (luigi)


Lesenswert?

Entferne die blind kopierte ISR und werte bei einer Flanke an Signal A 
das signal B aus. Ist B high dann incrementierst du deinen Zähler, ist B 
low dacrementierst du ihn.
Ganz einfach und viel kleiner als dein Code. So ist das bei den 
Billig-Drehencodern auch gedacht...

von Wolfgang (Gast)


Lesenswert?

wendelsberg schrieb:
> Entprellung ist das Stichwort, nach dem Du suchst.

Du hast Quadraturencoder nicht verstanden. Suche mal nach Gray-Code.

von Jan H. (janiiix3)


Lesenswert?

Falk B. schrieb:
> So.
>
>
1
> int8_t encode_read2( void )         // read two step encoders
2
> {
3
>   int8_t val;
4
> 
5
>   cli();
6
>   val = enc_delta;
7
>   enc_delta = val & 1;
8
>   sei();
9
>   return val >> 1;
10
> }
11
>

Besten dank.

von MaWin (Gast)


Lesenswert?

Leo B. schrieb:
> Entferne die blind kopierte ISR und werte bei einer Flanke an
> Signal A das signal B aus. Ist B high dann incrementierst du deinen
> Zähler, ist B low dacrementierst du ihn.
> Ganz einfach und viel kleiner als dein Code. So ist das bei den
> Billig-Drehencodern auch gedacht...

Ach du Scheisse, ein Gehirnloser der auch nach dem hundertsten Thread 
immer noch nicht weiss wie ein Drehencoder zuverlässig auszuwerten ist. 
Da ist Jan ja schon erheblich weiter.

Jan H. schrieb:
> Und muss feststellen das mein Encoder zwei Impulse liefert bis er
> einrastet.

Und du wolltest mit der Abänderung von table dagegen angehen ? Damit 
funktioniert es aber nicht mehr. Folge lieber dem Vorschlag von Peter C.

von H.Joachim S. (crazyhorse)


Lesenswert?

Leo B. schrieb:
> werte bei einer Flanke an Signal A
> das signal B aus. Ist B high dann incrementierst du deinen Zähler, ist B
> low dacrementierst du ihn.

Das hört wohl niemals auf :-)

Ist aber auch gemein, ich bin selbst vor Jahren darauf hereingeflogen. 
Man sieht so schöne Zustandsdiagramme, programmiert drauf los - und es 
funktioniert mit ordentlichen Encodern sogar erst mal super, leider 
nicht lange.
Meine Jura-Kaffemaschine leidet auch an dem Problem, ein Encoder-Tausch 
half nur kurzzeitig. Irgendwann bau ich einen Tiny25 zwischen Encoder 
und Kaffemaschinen-Elektronik :-)

von CNC (Gast)


Lesenswert?


von Toxic (Gast)


Lesenswert?

CNC schrieb:
> Beitrag "Drehencoder bullet proof und einfach"

Diesen Thread hatte ich vor ca. Wochen erstmals gesehen und habe den 
dortigen Buxtronic-Link in Anspruch genommen:
http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html

Funktioniert tadellos und dies mit einem 10 Jahre alten rostigen Rotary
Allerdings ist dem Buxtronic ein kleiner Tabellen-Typo unterlaufen,der 
in einem seiner Beispiele dazu fuehrte ,dass egal wie man den Encoder 
bewegte immer nur eine Richtung ausgab.Dies Korrektur kann jeder,der den 
Code einsetzen will selbst durchfuehren .(Ich schau jetzt da nicht mehr 
nach,weil sich in der Regel sowieso niemand dafuer interessiert)
Man kann mit seinem Code auch sehr einfach (durch "Kommentierung")nur 
einen Impuls oder 2 Impulse pro Rastung erzeugen.

von Norbert H. (nobi)


Lesenswert?

Ich mag das Tabellengedöns nicht beim Drehencoder.
Mann kann auch folgendermaßen vorgehen:

nach Flanke an A erkannt (egal ob fallend oder steigend):
wenn A und B gleich sind Richtung 1  (pos++)
wenn A und B unterschiedlich sind Richtung 2 (pos--)

nach Flanke an B erkannt:
wenn A und B gleich sind Richtung 2  (pos--)
wenn A und B unterschiedlich sind Richtung 1 (pos++)

Mann kann jetzt sehr schön A und B als Edge Triggert Interrupt einlesen, 
(oder natürlich auch pollen) und ohne sich sehr viel von der 
Vorgeschichte zu merken die Richtung bestimmen.


hab ich mal so implementiert, und funktioniert einwandfrei.

von MaWin (Gast)


Lesenswert?

Norbert H. schrieb:
> Ich mag das Tabellengedöns nicht beim Drehencoder.

Es geht nicht um mögen, sondern es geht um zuverlässige Programme

> hab ich mal so implementiert, und funktioniert einwandfrei.

In jedem Incrementalencoderthread kommen irgendwelche Noobs aus den 
Höhlen gekrochen, die das letzte Jahrhundert nicht mitbekommen haben, 
daß ihre Lösung falsch ist und die richtige Lösung schon lange bekannt 
ist.

> Mann kann jetzt sehr schön A und B als Edge Triggert Interrupt einlesen,

Man KANN keine Kontakte als Interrupt verwenden, weil Kontaktprellen 
schneller sein kann als die Interrupt-Abarbeitungszeit und daher beim 
prellen einzelne Flankenwechselsinterrupts verloren gehen und daher 
falsch zählt.

Nein, DEINER zählt ja nicht falsch (als ob du das merken würdest..), 
DEINER Ist ja auch der grösste Incrementalencoder, noch ist er ja nicht 
verstaubt.

Man müsste die Kontakte vorher mit externer Hardware entprellen.

Der Algorithmus

> nach Flanke an A erkannt (egal ob fallend oder steigend):
> wenn A und B gleich sind Richtung 1  (pos++)
> wenn A und B unterschiedlich sind Richtung 2 (pos--)
> nach Flanke an B erkannt:
> wenn A und B gleich sind Richtung 2  (pos--)
> wenn A und B unterschiedlich sind Richtung 1 (pos++)

ist jedoch nicht grundfalsch, er funktioniert wenn er per 
ZEITGEBER/Hauptschleife aufgerufen wird.

von Norbert H. (nobi)


Lesenswert?

Das mit dem Interrupt ist ein Beispiel, natürlich muss der Konntakt 
dementsprechend angeschlossen sein, und ist Durch das Tastenprellen 
nicht sinnvoll, wenn mann keine HW-Entprellung hat, - ein kleines R-C 
wirkt da manchmal Wunder.
Zum Anderen würde ich mich nicht nur auf den Interrupt Verlassen, mann 
kann auch innerhalb des Interrupts noch einmal eine 
plausibilitätsprüfung Durchführen, un den eingenen Port kontrollieren ob 
sich der tatsächlich geändert hat - so ist dieser zumindest 2mal gelesen 
worden.

Zum anderen hab ich ja auch geschrieben, dass man die Ports auch pollen 
kann, dann kannst Du alle entprellalgorythmen einsetzten die Du magst, 
wenn du schnell genug bist.

-> letztlich ist es mit egal, wie die Flanke zuverlässig erkannt wird.
Dies muß bei jedem Algorythmus irgendwie zuverlässig geschehen.

Ich wollte nur eine möglichkeit aufzeigen daraus die Richtung zu 
erkennen.

Bei meiner Anwendung wurde der Drehimpulsgeber durch einen Motor gedreht 
zur Positionserkennung einer Spindel, da kommen schon ein paar KHz 
zusammen, und Du musst ziemlich schnell sein, wenn Du da noch eine 
größere Softwareentprellung einbauen willst.

von Karl M. (Gast)


Lesenswert?

Hallo,

es geht auch schnell wie die Herren, speziell auch Falk Brunner, hier 
zeigt haben:
Beitrag "Versetzte Rechtecksignale auswerten, kein drehgeber"

von Peter B. (funkheld)


Lesenswert?

Mein Drehencoder zählt auch von einem Rastpunkt zum anderen Rastpunkt 2 
weiter. Wenn ich ihn aus dem 1. Rastpunkt löse zählt er 1 und wenn er im 
nächsten Rastpunkt fast einrastet zählt er 1. Hat nichts mit Prellung 
und Kaputt zu tun. Sonst könnten sie ja nicht vorwärts und rückwärts 
unterscheiden.
1
class encoder {
2
public:
3
  encoder(int Pin1, int Pin2);  //Constructor
4
  int read();
5
private:
6
  int _Pin1, _Pin2, _Val1, _Old_Val1, _Val2;
7
};
8
9
encoder::encoder(int Pin1, int Pin2) {
10
  _Pin1 = 28;
11
  _Pin2 = 29;
12
  digitalWrite(_Pin1,HIGH);
13
  digitalWrite(_Pin2,HIGH);
14
  _Old_Val1 = digitalRead(_Pin1);
15
}
16
17
int encoder::read(){
18
  int rc = 0;  
19
  _Val1 = digitalRead(_Pin1);
20
  _Val2 = digitalRead(_Pin2);
21
  if (_Val1 != _Old_Val1) {
22
    if (_Val1 == _Val2) rc = 1; 
23
    else rc = -1;
24
    _Old_Val1 = _Val1;
25
  }
26
  return rc;  
27
}
28
29
encoder my_encoder(11,12);
30
31
int encoder_val;
32
int my_Value = 100;
33
34
void setup() {
35
  Serial.begin(9600);
36
}
37
38
void loop() {
39
  encoder_val = my_encoder.read();
40
  if (encoder_val != 0){
41
    my_Value += encoder_val;
42
    Serial.println(my_Value);
43
  }
44
  delay(5);       
45
}

von Falk B. (falk)


Lesenswert?

@  Karl M. (Gast)

>es geht auch schnell wie die Herren, speziell auch Falk Brunner, hier
>zeigt haben:
>Beitrag "Versetzte Rechtecksignale auswerten, kein drehgeber"

Bitte keine Lobgesänge auf die Falschen, die Idee kam von Ulrich, ich 
hab das nur ins AVR-Studio eingehackt und assembliert. Ich war nur der 
Wasserträger.

von Jan H. (janiiix3)


Lesenswert?

Ich weiß nicht was ihr gegen die Tabellen Variante habt. Funktioniert 
doch prima. Schneller kann man mit der Hand sowieso nicht drehen.

Auch wenn ich die Tabelle noch nicht ganz verstanden habe.

von MaWin (Gast)


Lesenswert?

Norbert H. schrieb:
> da kommen schon ein paar KHz zusammen, und Du musst ziemlich schnell sein

Wir hatten schon endlose Threads zum Drehencoder,
die polling Methode ist in Assembler auf einem 16MHz AVR in der Lage,
4 Drehgeber mit bis zu 960000kHz abzutasten und die Positionen zu 
zählen, da hast du mit Interrupts keine Chance, denn du muss ja 
sicherstellen, daß auch im ungünstigsten Fall alle Interrups 
abgearbeitet werden können.

von Noob (Gast)


Lesenswert?

MaWin schrieb:
> Wir hatten schon endlose Threads zum Drehencoder,
> die polling Methode ist in Assembler auf einem 16MHz AVR in der Lage,
> 4 Drehgeber mit bis zu 960000kHz abzutasten

Wahnsinn!
Und jeden Kanal kann man mit 10 kSamples abtasten?
Wie werden denn die Indeximpulse des Encoders ausgewertet?

von MaWin (Gast)


Lesenswert?

Noob schrieb:
> Wahnsinn!
> Und jeden Kanal kann man mit 10 kSamples abtasten?

Mit 960000 Samples/Sekunde, ohne das hineingeschlichene k.

> Wie werden denn die Indeximpulse des Encoders ausgewertet?

http://www.dse-faq.elektronik-kompendium.de/dse-faq.htm#F.29

von W.S. (Gast)


Lesenswert?

MaWin schrieb:
> Ach du Scheisse, ein Gehirnloser der auch nach...

Ach, wir üben uns mal wieder in gehobenem Disput, oder?

Das Thema ist schon lange durch - und wer es nicht gelernt hat, seine 
Signale vor der Auswertung zuverlässig zu konditionieren (hier: sprich 
per HW zu entprellen), der ist selber schuld.

Sowas ist ne Grundkenntnis für Elektroniker, die man einfach 
voraussetzen muß - und wer das nicht kann (z.B. Programmierer), sollte 
seine Nase lieber mal ein paar Wochen in den Tietze-Schenk stecken.

W.S.

von Axel S. (a-za-z0-9)


Lesenswert?

Jan H. schrieb:
> Ich weiß nicht was ihr gegen die Tabellen Variante habt. Funktioniert
> doch prima. Schneller kann man mit der Hand sowieso nicht drehen.

Mit Geschwindigkeit hat das auch nichts zu tun. Tatsächlich sind 
Tabellen sehr oft die schnellere Variante.

> Auch wenn ich die Tabelle noch nicht ganz verstanden habe.

Ist doch trivial. Zu jedem der 4 Zustände des Encoders gibt es zwei 
erlaubte Nachbarzustände. Falls der Encoder in den verbotenen Zustand 
übergehen sollte, dann bedeutet das daß man zu langsam abtastet. Die 
Tabelle gibt nun für jeden Zustand und den unmittelbar vorher gelesenen 
Zustand an, ob man um 1 vorwärts oder rückwärts zählen muß.

Am besten denkt man sich die Tabelle als 4x4 Matrix mit Zeilen und 
Spalten in der Graycode(!) Reihenfolge. Auf der Hauptdiagonale stehen 
dann logischerweise Nullen (von einem Zustand zu sich selber wird nicht 
weitergezählt). Auf den Positionen oberhalb und unterhalb der Diagonale 
(mit Überlauf über den Rand der Matrix) sind die erlaubten Nachbar- 
positionen mit -1 und +1. Bei Viertelschrittauswertung alle 4. Bei 
Halbschrittauswertung nur zwei und bei Vollschrittauswertung nur einer.

Die fertige Tabelle im Code sieht geringfügig anders aus, weil sie eben 
nicht nach dem Graycode, sondern nach dem Binärcode sortiert ist.

von Lurchi (Gast)


Lesenswert?

Ob man jetzt die Tabelle nutzt oder von Hand mit logischen Verknüpfungen 
rechnet ist Geschmackssache. Je nach  µC, Pinbelegung und Compiler ist 
mal das eine oder andere schneller bzw. kürzer.

Wenn einem die Auflösung zu hoch ist, könnte man auch die Tabelle 
anpassen und so nur jeden 4. Übergang zählen. Es gibt halt auch noch 
eine Form für 1/4 der Auflösung.

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.