Drehimpulsgeber mit Rasterstellung bei 00/11 auswerten

#287205
Lesenswert?

Hier mal ein neuer Ansatz zum Auswerten von Drehimpulsgebern....

In der Praxis hat man häufig Drehimpulsgeber (rotary switches) die
Raststellungen bei 00 und 11 haben. Die Zwischenstellungen 01 der 10
treten nur dynamisch während der Drehung auf. Solche Geber sind z.B.
als Eingabeinstrument üblich.

Der sehr gute Code aus diesem Thread
http://www.mikrocontroller.net/forum/read-4-37992.html#new
hat passt für diesen Typ nicht so gut denn:

a) Bei den oben beschriebenen Drehimpulsgebern zählt er doppelt weil er
bei jedem Wechsel inc- oder decrementiert. D.h. wird um eine
Raststellung weitergedreht, zählt der Zähler +/-2.

b) Wärend des Kontaktprellens ist der Ausgangswert nicht konstant
sondern kann +/-1 wechseln. Das kann ein Problem sein, wenn das
Anwenderprogram ungünstig ausliest.

c) Er ist nicht unbedingt einfach zu verstehen.

Hier nun mein Code für AVR:

  static uint8_t last_state = 0;
  static uint8_t last_cnt = 0;
  uint8_t new_state;

  new_state=PINE & (_BV(PINE4) | _BV(PINE3));
  if ((new_state^last_cnt)==(_BV(PINE4) | _BV(PINE3)) )
  {
    if ((new_state ^ last_state)==_BV(PINE4))
      enc_delta+=1;
    else
      enc_delta-=1;
    last_cnt=new_state;
  }
  last_state=new_state;

Er funktioniert folgendermassen:
- Am Anfang werden die zwei Input-Ports eingelesen.
- Dann wird geprüft, ob sich beide Bit's unterscheiden
- Wenn nein wars das schon
- Wenn ja, schauen wir woher wir kommen
  kommen wir von rechts (je nach Beschaltung) zählen wir hoch, sonst
runter
- Dann speichern wir uns noch den State bei dem wir gezählt haben
- Am Ende merken wir uns den ausgewerteten State damit wir nächstes mal
wissen woher wir kommen

Dabei wird natürlich automatisch entprellt.

Have Fun.
Angehängte Dateien:
Gast #287207
Lesenswert?

Ich schreibe das hier mal auf basis von Port B Bit 0 und 1.
1. Zwischenspeichern von PB0 & PB1
2. PB0 XOR PB1 = 0 -> nix passiert nix tun
               = 1 -> es wurde gedreht
3. PB0 XOR Zwischenspeicher PB0 = 1 es wurde nach links gedreht
                                 = 0 es wurde nach rechts gedreht
4. (Entprellung)
   wenn bei 3. das Ergebnis 1 war dann nicht neu bei 1. anfangen bevor
  nicht auch PB1 den status gewechselt hat,
   wenn bei 3. das Ergebnis 0 war dann nicht neu bei 1. anfangen bevor
  nicht PB0 den status gewechselt hat.
Gast #287208
Lesenswert?

@Techniker:

_BV ist letztlich nur ein Makro:(1 << (x)).

Schöner wäre es, wenn Du _BV nicht nimmst, sondern das Makro
ausschreibst. Dann ist es portabler (andere Compiler).
Mit _BV wird der Code allenfalls ein bißchen leserlicher.
#287210
Lesenswert?

Hier, nur kopiert, nicht näher angeschaut....

a) Optimization for size

70:         new_state=PINE & (_BV(PINE4) | _BV(PINE3));
+000003A4:   91800000    LDS     R24,0x0000       Load direct from data
space
+000003A6:   7188        ANDI    R24,0x18         Logical AND with
immediate
+000003A7:   8389        STD     Y+1,R24          Store indirect with
displacement
71:         if ((new_state^last_cnt)==(_BV(PINE4) | _BV(PINE3)) )  //
we are either in main state 00 or 11
+000003A8:   8199        LDD     R25,Y+1          Load indirect with
displacement
+000003A9:   9180014C    LDS     R24,0x014C       Load direct from data
space
+000003AB:   2789        EOR     R24,R25          Exclusive OR
+000003AC:   3188        CPI     R24,0x18         Compare with
immediate
+000003AD:   F4A1        BRNE    PC+0x15          Branch if not equal
73:           if ((new_state ^ last_state)==_BV(PINE4))
+000003AE:   8189        LDD     R24,Y+1          Load indirect with
displacement
+000003AF:   9190014B    LDS     R25,0x014B       Load direct from data
space
+000003B1:   2789        EOR     R24,R25          Exclusive OR
+000003B2:   3180        CPI     R24,0x10         Compare with
immediate
+000003B3:   F431        BRNE    PC+0x07          Branch if not equal
74:             enc_delta+=1;
+000003B4:   9180014A    LDS     R24,0x014A       Load direct from data
space
+000003B6:   5F8F        SUBI    R24,0xFF         Subtract immediate
+000003B7:   9380014A    STS     0x014A,R24       Store direct to data
space
+000003B9:   C005        RJMP    PC+0x0006        Relative jump
76:             enc_delta-=1;
+000003BA:   9180014A    LDS     R24,0x014A       Load direct from data
space
+000003BC:   5081        SUBI    R24,0x01         Subtract immediate
+000003BD:   9380014A    STS     0x014A,R24       Store direct to data
space
77:           last_cnt=new_state;
+000003BF:   8189        LDD     R24,Y+1          Load indirect with
displacement
+000003C0:   9380014C    STS     0x014C,R24       Store direct to data
space
79:         last_state=new_state;
+000003C2:   8189        LDD     R24,Y+1          Load indirect with
displacement
+000003C3:   9380014B    STS     0x014B,R24       Store direct to data
space
+000003C5:   9621        ADIW    R28,0x01         Add immediate to
word

b) Optimization for speed
70:         new_state=PINE & (_BV(PINE4) | _BV(PINE3));
+000003A4:   91800000    LDS     R24,0x0000       Load direct from data
space
+000003A6:   7188        ANDI    R24,0x18         Logical AND with
immediate
+000003A7:   8389        STD     Y+1,R24          Store indirect with
displacement
71:         if ((new_state^last_cnt)==(_BV(PINE4) | _BV(PINE3)) )  //
we are either in main state 00 or 11
+000003A8:   8199        LDD     R25,Y+1          Load indirect with
displacement
+000003A9:   9180014C    LDS     R24,0x014C       Load direct from data
space
+000003AB:   2789        EOR     R24,R25          Exclusive OR
+000003AC:   3188        CPI     R24,0x18         Compare with
immediate
+000003AD:   F4A1        BRNE    PC+0x15          Branch if not equal
73:           if ((new_state ^ last_state)==_BV(PINE4))
+000003AE:   8189        LDD     R24,Y+1          Load indirect with
displacement
+000003AF:   9190014B    LDS     R25,0x014B       Load direct from data
space
+000003B1:   2789        EOR     R24,R25          Exclusive OR
+000003B2:   3180        CPI     R24,0x10         Compare with
immediate
+000003B3:   F431        BRNE    PC+0x07          Branch if not equal
74:             enc_delta+=1;
+000003B4:   9180014A    LDS     R24,0x014A       Load direct from data
space
+000003B6:   5F8F        SUBI    R24,0xFF         Subtract immediate
+000003B7:   9380014A    STS     0x014A,R24       Store direct to data
space
+000003B9:   C005        RJMP    PC+0x0006        Relative jump
76:             enc_delta-=1;
+000003BA:   9180014A    LDS     R24,0x014A       Load direct from data
space
+000003BC:   5081        SUBI    R24,0x01         Subtract immediate
+000003BD:   9380014A    STS     0x014A,R24       Store direct to data
space
77:           last_cnt=new_state;
+000003BF:   8189        LDD     R24,Y+1          Load indirect with
displacement
+000003C0:   9380014C    STS     0x014C,R24       Store direct to data
space
79:         last_state=new_state;
+000003C2:   8189        LDD     R24,Y+1          Load indirect with
displacement
+000003C3:   9380014B    STS     0x014B,R24       Store direct to data
space
#287212
Lesenswert?

Hallo Andi,

ich verwende 500Hz Abtastrate. Läuft allerdings nur als manuelles
Eingabegerät. Dass heisst, es stört eigentlich nicht, wenn beim
schnellen drehen per Hand Pulse verloren gehen. Der User kann beim
Kurbeln sowieso nicht mitzählen... Bei der langsamen Feineinstellung -
solange man mitzählen kann - reichen die 500Hz.

Grüße
  Christian
Gast #287214
Lesenswert?

@Christian

Aeh, das kapier ich nicht :

 if ((new_state^last_cnt)==(_BV(PINE4) | _BV(PINE3)) )
 {
   if ((new_state ^ last_state)==_BV(PINE4))

Nur einer dieser beiden Ausdruecke kann wahr sein. Folglich kann die
Routine nur rueckwaerts zaehlen, oder ?
#287220
Lesenswert?

Hallo,

habe den Code so erweitert, dass man auch mehrere Drehgeber einlesen
kann.
1
void evalRots(unsigned int rotData)
2
//Erkennt die Umdrehungen der Encoder
3
{
4
  static unsigned int last_state = 0;
5
  static unsigned int last_cnt = 0;
6
  
7
  for(unsigned char i=0; i< 12; i+=2)    //Encoder Anzahl: 6
8
  {
9
    if (((rotData^last_cnt) & (1 << i))  && ((rotData^last_cnt) & (1 <<
10
(i+1))))
11
    {
12
      if ((rotData ^ last_state) & (1 << i))
13
        enc_delta[(i>>1)]--;
14
      else
15
        enc_delta[(i>>1)]++;
16
      
17
      if((last_cnt^rotData) & (1 << i))      //bits verschieden?
18
        last_cnt^=(1 << i);            //togglen
19
      if((last_cnt^rotData) & (1 << (i+1)))    //bits verschieden?
20
        last_cnt^=(1 << (i+1));          //togglen
21

22
    }
23
    if((last_state^rotData) & (1 << i))        //bits verschieden?
24
      last_state^=(1 << i);            //togglen
25
    if((last_state^rotData) & (1 << (i+1)))      //bits verschieden?
26
      last_state^=(1 << (i+1));          //togglen
27
  }
28
}

Bei mir befinden sich die Graycode werte in einem Integer, da ich die
Encoder über zwei schieberegister einlese. rotData kann aber auch
einfach in Port des uc sein. Der Code macht im prinzip genau das
gleiche wie der Orginalcode des Threads, wahrscheinlich lässt sich da
noch einiges vereinfachen, laufen und funktionieren tut er aber.

Tubbu
Gast #287222
Lesenswert?

Habe einen Drehimpulsgeber von Alps (STEC12E06 von Conrad) der mit jeder
Rasterstellung den Zähler um 4 weiterzählt, was für meinen Fall nicht
gewünscht ist. Ich habe das Beispiel von Christian ausprobiert, leider
nicht mit dem gewünschten Erolg. Der Code aus dem dem Thread

http://www.mikrocontroller.net/forum/read-4-37992.html#

von Peter Dannegger lässt sich auch dann prima verwenden wenn zwischen
den Rasterstellungen 2 oder 4 Impulse kommen, aber nur einer gezählt
werden soll.

ISR(TIMER0_OVF_vect)
{
  static char enc_last = 0x01;
  char i = 0;

    if( PHASE_A )
      i = 1;

    if( PHASE_B )
      i ^= 3;        // convert gray to binary

    i -= enc_last;      // difference new - last
    if( i & 1 ){      // bit 0 = value (1)
      enc_last += i;      // store new as next last
    enc_delta += (i & 2) - 1;  // bit 1 = direction (+/-)
    if (!(enc_delta % 4)) {  // nur jeden 4. Schritt zählen
    if ((i & 2)) {    // prüfen ob auf oder ab
      count++;    // 0-255
    }
    else {
      count--;    // 255-0
    }
    }
    }
}

Läuft super.

Arne
Gast #287223
Lesenswert?

Ich habe erfolglos versucht den Code hier zu verwenden.

Wäre jemand so freundlich mir das ganze nochmals klar und deutlich zu
erklären? Die Codeschnippsel oben sind eher verwirrend!!


last_cnt ist die Bitkombination bei der zuletzt gezählt wurde (00) oder
(11)

last_state ist der letzte Zustand (10)/(01) d.h. woher ich komme

wo wird bei 00 und 11 etwas gezählt? da soll ja gerade nichts
passieren...


Mit freundlichen Grüssen
Gast #287224
Lesenswert?

Dies ist mein Code:

void encoder(void)
{

struct
{
 unsigned int speicher_A :1;
 unsigned int speicher_B :1;
}encoder;

if(PF.PORT.BIT.B1 == PF.PORT.BIT.B2)
{
 encoder.speicher_A = PF.PORT.BIT.B1;
 encoder.speicher_B = PF.PORT.BIT.B2;
}

if(PF.PORT.BIT.B1 ^ PF.PORT.BIT.B2)  //Bits unterschiedlich?
 {
   if(encoder.speicher_B ^ PF.PORT.BIT.B2) // Hat Bit2 geändert?
    {
      while(PF.PORT.BIT.B1 == encoder.speicher_A); //warten
      LCD_clear(0);
      LCD_writeString("Links", 0, 0,0);

    }

   else
    {
      while(PF.PORT.BIT.B2 == encoder.speicher_B);
      LCD_clear(0);
      LCD_writeString("Rechts", 0, 60,0);
    }
 }
}


90 % der Fälle funtkioniert er, jedoch werden manchmal Drehungen nicht
erkannt (auch wenn sie sehr langsam gemacht wurden). Es muss also
irgendwo ein Fehler sein! Ausserdem finde ich das mit der while
schleife ebenfalls nicht gut, aber irgendwie muss ich ja die Änderung
des andern Bits abwarten, oder?

MFG
Gast #287225
Lesenswert?

Irgendwie kann ich bei deinem Code keine grosse Ähnlichkeit mit dem
Original erkennen...

Vielleicht ist wichtig, dass _BV(PINE4) und _BV(PINE3) verschieden
sind. _BV(PINE4) bedeutet z.B. (1<<3) d.h. 8, und _BV(PINE3) = 1<<2 =
4. Ist bei deinem PF.PORT.BIT.B1 vermutlich nicht so?

Ausserdem kostet dein LCD_clear() und LCD_write() vmtl. soviel Zeit,
dass die Drehpulse verloren gehen. Der Code sollte schon mit einer
gewissen Poll-Frequenz laufen (bei mir 400Hz).

Hier nocheinmal der Original-Code damit man nicht scrollen muss...

void encoder(void)
{
  static uint8_t last_state = 0;
  static uint8_t last_cnt = 0;
  uint8_t new_state;

  new_state=PINE & (_BV(PINE4) | _BV(PINE3));
  if ((new_state^last_cnt)==(_BV(PINE4) | _BV(PINE3)) )
  {
    if ((new_state ^ last_state)==_BV(PINE4))
      enc_delta+=1;
    else
      enc_delta-=1;
    last_cnt=new_state;
  }
  last_state=new_state;
}
#531947
Lesenswert?

hallo

ich versuche gerade das zum laufen zu bekommen
scheitere aber kläglich

hier mal ein paar schnipsel:



// belegung des Rotary Encoders
#define ROTARY_DDR      DDRD
#define ROTARY_PIN      PIND
#define ROTARY_PORT     PORTD
#define ROTARY_A        5
#define ROTARY_B        6
#define ROTARY_P        7

int enc_delta=0;

void encoder(void)
{
  static uint8_t last_state = 0;
  static uint8_t last_cnt = 0;
     uint8_t new_state;

  new_state=ROTARY_PIN & ((1<<ROTARY_A) | (1<<ROTARY_B));

  if ((new_state^last_cnt)== (1<<ROTARY_A) | (1<<ROTARY_B))
    {

  if ((new_state ^ last_state)==(1<<ROTARY_A))
    enc_delta+=1;
  else
    enc_delta-=1;

  last_cnt=new_state;
  }
  last_state=new_state;
}


int main (void)
{
     ROTARY_DDR &=~ (1<<ROTARY_A)|(1<<ROTARY_B)|(1<<ROTARY_P);
     ROTARY_PORT |= (1<<ROTARY_A)|(1<<ROTARY_B)|(1<<ROTARY_P);



  for(;;)
  {

        encoder();

        itoa(enc_delta,out,10);
  lcd_gotoxy(13,0);
  lcd_puts(out);

        }

}



es kommt aber leider nur ein extrem schnelles zählen auf das LCD
new_state und last_state sind immer auf 0
es passiert nix bei drehung

könnte mir bitte jemand sagen warum ?
hab schin gesucht , finde aber nix

Danke
#532775
Lesenswert?

hi

also ich habe es nun zum laufen bekommen
zwar mit anderem code

da dieser aber immer 2 schritte zählt  teile ich das ganze immer durch 2


#define ROTARY_DDR      DDRD
#define ROTARY_PIN      PIND
#define ROTARY_PORT     PORTD
#define ROTARY_A        5
#define ROTARY_B        6
#define ROTARY_P        7

volatile int enc_delta =0 ;


SIGNAL (SIG_OVERFLOW0)
{
  static char enc_last  = 0x01;
  char     i       = 0;
  if( (ROTARY_PIN & (1<<ROTARY_B) ))
    i = 1;
  if( (ROTARY_PIN & (1<<ROTARY_A) ))
    i ^= 3;
    i -= enc_last;
  if( i & 1 )
    {
    enc_last += i;
      enc_delta += (i & 2) - 1;
      }
}
Gast #999996
Lesenswert?

Hallo,

habe hier nen Drehimpulsgeber (noname), der immer in der Raststellung 00 
hat.
Dadurch kann ich mit dem hier vorhandenen Code keine Auswertung machen.

Das Ding zählt halt nur in eine Richtung.

Also, R ist die Raststellung :
links  (R)00 - 01 - 11 - 01 - (R)00
rechts (R)00 - 10 - 11 - 10 - (R)00

Ich bitte mal um ne kleine hilfe.

gruss tomgr
Gast #1559628
Lesenswert?

Ich verwende Encoder mit 30 oder 40iger Auflösung.
Da kriegt der AVR ganz schön was zu tun.
Sollte man den kleinsten Eingang über IRQ aufnehmen,
anstatt mit hoher Samplerate zu arbeiten?

Das Softwareentprellen ist auch nicht ganz ohne.
Habe ich das auch bei Drehgebern mit HALL-IC?

P.S.
Habe meine Drehgeberreste jetzt in EBay eingestellt.
Wer noch welche braucht...
Gast #1985831
Lesenswert?

Hi folks,

I´m very sorry but when I see this : "_BV(PINE4) | _BV(PINE3" . I´m just 
wondering how it functions.

Because you cannot write in the register PINX.

Or I´m wrong ? or I´m messed up ? somebody has to explain me that...
#1986964
Lesenswert?

@  wilfried mühlenhoff (Gast)

>Ich verwende Encoder mit 30 oder 40iger Auflösung.
>Da kriegt der AVR ganz schön was zu tun.

Nö.

>Sollte man den kleinsten Eingang über IRQ aufnehmen,
>anstatt mit hoher Samplerate zu arbeiten?

Nein, siehe Artikel Drehgeber.

>Habe ich das auch bei Drehgebern mit HALL-IC?

Ja. Siehe Artikel.
#3974858
Lesenswert?

Hallo,

ich habe den Code verwendet, nur ergibt sich bei mir bei der Abfrage

new_state ^ last_state = 11

Also ein unerwünschte Ergebnis, da ja nur 10 oder 01 erwartet wird.

Kann mir jemand helfen?
1
ISR( TIMER2_OVF_vect )
2
{
3

4
new_state = PINL & ((1<<5) | (1<<3))  ;  // save state
5

6
// Change in State on both important positions?
7
if (  (new_state ^ last_count) == ((1<<5 )|(1<<3))    ) 
8
  {
9
    // right or left turn?
10
    if(  (new_state ^ last_state) == (1<<3 ) )
11
      {enc_delta += 1;}
12
    else                               // Falle für unerwünschten Zustand
13
      {enc_delta -= 1;}
14

15
    enc_last_count = enc_new_state;    // save Bitcombination of last count
16
  }
17
enc_last_state = enc_new_state;        // save last state
18

19
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren