Forum: Compiler & IDEs Drehencoder entprellen, aber wie?


von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Hi,

in einem meiner Projekte verwende ich einen Drehencoder.

Der Encoder hat ne Rastung, pro Rasteinheit liefert er zwei Inkremente 
ab.

Prinzipell funktioniert die Auswertung. Angeschlossen ist der Drehgeber 
an zwei externe INTs (PinChange an AVR ATmega88).

Im Programm gibt es nun Probleme, weil das Ding prellt. Ne "normale" 
Entprellung kommt nicht in Frage, weil man ja keine Pulse verpassen 
will.

Das Problem ist, daß Linksdrehen (-) nicht die Umkehrung von 
Rechtsdrehen (+) ist, d.h. im Programm wird die Folge +-+ anders 
interpretiert als ++- anders als + etc.

Hat jemand ne Idee wie man das lösen könnte? Hier mal die 
C-Implementierung:
1
#ifndef _DREHGEBER_H_
2
#define _DREHGEBER_H_
3
4
#define DREH_INVALID -128
5
6
static inline char drehgeber_step(void);
7
static inline char drehgeber_job(void);
8
9
#if defined (__GNUC__) && defined (__AVR__)
10
#    include <avr/pgmspace.h>
11
#else
12
#    define PROGMEM
13
#    define pgm_read_byte(x) (*(x))
14
#endif // avr-gcc
15
16
// Aufrufen, wenn eine Flanke an einem der Ports festgestellt wurde,
17
// zB aus einer ISR heraus. 
18
char drehgeber_step ()
19
{
20
    static const char drehgeber_transitions[] PROGMEM = 
21
    {
22
         0,                       1,           -1, DREH_INVALID,
23
        -1,                       0, DREH_INVALID,            1, 
24
         1,            DREH_INVALID,            0,           -1, 
25
         DREH_INVALID,           -1,            1,            0
26
    };
27
    
28
    static uint8_t a_alt;
29
    uint8_t a = a_alt;
30
    
31
    if (IS_SET (PORT_DREH_A))        a |= (1 << 2);
32
    if (IS_SET (PORT_DREH_B))        a |= (1 << 3);
33
    
34
    a_alt = a >> 2;
35
    
36
    return (char) pgm_read_byte (& drehgeber_transitions[a]);
37
}
38
39
// Mein Drehgeber hat eine Rasterung und bei jeden Raster erzeugt er *zwei*
40
// Flanken. Man muss also die Anzahl der Schritte etwas mühsam durch 2 teilen.
41
char drehgeber_job(void)
42
{
43
    char step = drehgeber_step();
44
    char s = 0;
45
    
46
    if (DREH_INVALID != step)
47
    {
48
        static char tick;
49
        char t = step + tick;
50
        
51
        if (t >=  2)    s =  1;
52
        if (t <= -2)    s = -1;
53
        
54
        t -= s;
55
        t -= s;
56
        
57
        tick = t;
58
    }
59
    
60
    return s;
61
}
62
#endif // _DREHGEBER_H_


Was das Makro IS_SET macht sollte klar sein, aufgerufen wird 
drehgeber_job() in der PinChange ISR:
1
#include <inttypes.h>
2
#include <avr/interrupt.h>
3
4
#include "drehgeber.h"
5
6
static int8_t volatile steps; // vom Drehgeber
7
8
...
9
10
SIGNAL (SIG_PIN_CHANGE0)
11
{
12
    steps += drehgeber_job();
13
}
14
15
int main()
16
{
17
   ...
18
   // Leeraufruf zum Initialisieren
19
   drehgeber_step();
20
   ...
21
}

von Matthias L. (Gast)


Lesenswert?

>Im Programm gibt es nun Probleme, weil das Ding prellt. Ne "normale"
>Entprellung kommt nicht in Frage, weil man ja keine Pulse verpassen
>will.

Macht man im TImerinterrupt alle 1 bzw. 10ms.

Drehgeber: Beispielcode in C

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

#ARGL#

Hätt ich auch seber mal draufkommen können, das auszuprobieren...

Reagiert im 10ms-Job zwar merklich langsamer als vorher, aber wenigstens 
zählt er nicht mehr falsch...

EDIT

und @ 2.5kHz ist's so wie ich's mir vorstelle :-)

So einfach ist die Welt...

von D. S. (jasmin)


Lesenswert?

auf alle Fälle mit 0,1 uf keramik c von den pulskontaten nach masse.

das ist oft mehr als die halbe miete !!!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Die Schaltung ist fertig und keinPlatz mehr... mit der Poll-per-IRQ 
klappt's ja prima und mit minimaler Hardware :-)

von fantozzi (Gast)


Lesenswert?

Codevorschlag ohne Klimbim (für Encoder mit 2 Werten pro Rasterung im 
polling):

neu= PORTB&0b.0000.0011;   // einlesen der beiden Pins

if (alt!=neu)
{
//   Auswertung der Richtung
//   jede Richtung hat separaten Zähler
if(alt==0 && neu==1)zahler_down++;
if(alt==1 && neu==3)zahler_down++;
if(alt==3 && neu==2)zahler_down++;
if(alt==2 && neu==0)zahler_down++;

if(alt==0 && neu==2)zahler_up++;
if(alt==2 && neu==3)zahler_up++;
if(alt==3 && neu==1)zahler_up++;
if(alt==1 && neu==0)zahler_up++;

//wenn einer der Richtungszähler den Wert 2 hat
//wird er auf Null zurückgesetzt und der Hauptzähler bewegt
if(zahler_up   == 2)  {Hauptzahler++;    zahler_up=0;}
if(zahler_down == 2)  {Hauptzahler--;    zahler_down=0;}

//zum Entprellen:wenn ein Zähler den Wert 1 erreicht
//wird die Gegenrichtung zurückgesetzt
if(zahler_up == 1)  {zahler_down=0; }
if(zahler_down == 1)  {zahler_up=0;  }

alt=neu;
}


ich weiss der thread ist alt , ist bei google aber noch weit vorne wenn 
man nach --Drehencoder entprellen-- sucht

von Peter D. (peda)


Lesenswert?

fantozzi schrieb:
> Codevorschlag ohne Klimbim

Also 13 IFs, das ist ne riesen Menge Klimbim.

Der Code im obigen Link braucht kein einziges IF, der ist kurz und 
knackig (wenig Zyklen, wenig Flash).


Peter

von fantozzi (Gast)


Lesenswert?

:-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> Also 13 IFs, das ist ne riesen Menge Klimbim.

Hat ja auch 3,5 Jahre gedauert, bis er fertig war.

von Simon K. (simon) Benutzerseite


Lesenswert?

Dietmar Steiner schrieb:
> auf alle Fälle mit 0,1 uf keramik c von den pulskontaten nach masse.
>
> das ist oft mehr als die halbe miete !!!

Da der Thread offenbar bei der Suche noch so weit vorne ist:
Oben genannten Tipp bitte unter keinsten Umständen machen. Das ist 
allergröbster Pfusch und zerstört nicht nur die Kontakte, sondern 
erzeugt auch noch extreme Störungen.

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.