Drehencoder für Anfänger

Gast #1198612
Lesenswert?

Hallo liebes Forum.

Ich habe vor, einen Drehencoder STEC11B03 an den Atmega32 
anzuschliessen.
Es gibt bereits viele aufschlussreiche Beiträge im Forum zu diesem Thema
(Natürlich auch vom AVR-Meister peter dannegger) jedoch sind diese eher 
für
Fortgeschrittene und Profis.
Mein Problem: Wie wird der Encoder eingentlich angeschlossen (Pull-ups / 
Entprellung )???
Im Datenblatt finde ich keinen Anschlussplan...

Könnte mich jemand aufklären???

Liebe Grüße
#1198648
Lesenswert?

Sven B. wrote:

> Es gibt bereits viele aufschlussreiche Beiträge im Forum zu diesem Thema
> (Natürlich auch vom AVR-Meister peter dannegger) jedoch sind diese eher
> für
> Fortgeschrittene und Profis.

???
Meinst Du damit, Anfänger dürfen nur schlecht funktionierenden Code 
benutzen?



> Mein Problem: Wie wird der Encoder eingentlich angeschlossen (Pull-ups /
> Entprellung )???

Pullups oder interne Pullups des MC.
HW-Entprellung nein, stört sonst die SW-Entprellung.


> Im Datenblatt finde ich keinen Anschlussplan...

Einfach auf 2 LEDs legen und langsam drehen. Ändern sich beide Signale 
mal gleichzeitig, ists falsch. Es gibt nur 3 Anschlußmöglichkeiten.


Peter
Gast #1198719
Lesenswert?

Hallo Peter.
Danke für Deine Antwort.

Wäre diese Beschaltung (s. Anhang) richtig ?

Irgendwie tue ich mich schwer damit...


P.S. Nein Klaus, ich möchte hier niemanden veräppeln. Ich suche nur 
etwas Unterstützung. Du bist sicherlich auch nicht als Profi auf die 
Welt gekommen.


Liebe Grüße
Gast #1198737
Lesenswert?

Pull-Up-Widerstände beim Schalten gegen 5V sind nicht wirklich sinvoll.

Da der Mega die Pull-Ups schon eingebaut hat, lass alles Hühnerfutter 
einfach weg. 0V (GND) an den Encoder, drei Leitungen an die 
Mega-Eingänge, dort die internen Pull-Ups aktivieren, fertig. Die 
Eingänge sind dann zwar active low, das macht aber nichts, die 
Peter'schen Funktionen für Drehgeber und Taster berücksichtigen das.

Oliver
Gast #1198792
Lesenswert?

@ Klaus.

Sorry Klaus.

Sei mir bitte nicht böse, aber ich muß mich da völlig unverständlich
ausgedrückt haben.
Ich meinte eigentlich nicht die Beschaltung des Encoders hinsichtlich 
der Entprellung und Pullups.

Siehe mein Anhang.
Ok. Ich habe anfangs viel komplizierter gedacht als es ist...
Pullups, entprellung etc.

Nicht gleich böse sein. :o)

Gruß
Gast #1199821
Lesenswert?

Hallo!

Ich habe nun den Drehencoder richtig angeschlossen und versuche
den in Gang zu kriegen. Vergeblich.
Wie kann ich denn auf dem einfachsten Weg den Drehencoder auf
Funktion überprüfen. ( Habe leider kein Ossi da )

Diesen Code nutze ich:
http://www.mikrocontroller.net/attachment/highlight/22223

Peter Dannegger schrieb :
Einfach auf 2 LEDs legen und langsam drehen. Ändern sich beide Signale
mal gleichzeitig, ists falsch. Es gibt nur 3 Anschlußmöglichkeiten.
Leider leuchten die LED´s sporadisch (wenn dann beide gleichzeitig) auf.

Beim Anschliessen eines LCD´s bekomme ich Zahlen wie -20 etc. die sich 
wahrlos ändern??

Hat Jemand noch ein wenig Geduld übrig?

Gruß
Moderator Persönliche Seite #1199842
Lesenswert?

Peter Dannegger wrote:

> Einfach auf 2 LEDs legen und langsam drehen. Ändern sich beide Signale
> mal gleichzeitig, ists falsch.

Kann schwierig werden.  Ich habe Encoder, bei denen beide Signale
sich optisch immer gleichzeitig verändern.  Erst wenn man etwas
zeitlich besser Auflösendes (Oszi im Einmal-Trigger-Modus oder LA)
anschließt, sieht man den Versatz.  Das liegt daran, dass die zwischen
den Rastungen ziemlich kräftig weiter gezogen werden.

Dafür war ich am LA-Bild total erstaunt.  Das Prellen war durchweg
im Bereich von maximal einigen 100 ns, und oft genug war mit den
10 ns, die mein alter LA auflöst, gar kein Prellen feststellbar.
Gast #1199871
Lesenswert?

ok.

ich habe ein LCD und versuche die Signaländerungen auf dem LCD 
auszugeben.
( In diesem Fall : enc_delta; )


Controllerfrequenz : 16 MHz
Drehencoder PORTB: A - Pin0; B - Pin1

Code :
1
#include <avr\io.h>
2
#include <avr\interrupt.h>
3
#include <avr\signal.h>
4

5
#include "glcd.h"
6
#include "font_small.h"
7

8
#define PHASE_A  (PINB & 1<<PINB0)  // PINB.0
9
#define PHASE_B (PINB & 1<<PINB1)  // PINB.1
10

11

12
volatile char  enc_delta;    // -128 ... 127
13

14

15
int main( void )
16
{
17
  lcd_init();
18
  lcd_clear();
19
  lcd_set_cursor(0,LINE0);
20
  lcd_puts(small_font,"LCD Test ist OK");
21

22
  TCCR0 = 1<<CS01;      //divide by 8 * 256
23
  TIMSK = 1<<TOIE0;      //enable timer interrupt
24

25
  sei();
26
  for(;;)
27
  {
28
  lcd_set_cursor(0,LINE1);
29
  lcd_puts(small_font,enc_delta);
30
  }
31
}
32

33

34
SIGNAL (SIG_OVERFLOW0)
35
{
36
  static char enc_last = 0x01;
37
  char i = 0;
38

39
  if( PHASE_A )
40
    i = 1;
41

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

45
  i -= enc_last;      // difference new - last
46

47
  if( i & 1 ){        // bit 0 = value (1)
48
    enc_last += i;      // store new as next last
49

50
    enc_delta += (i & 2) - 1;    // bit 1 = direction (+/-)
51
  }
52
}


Es tut sich leider nichts...

Auf dem LCD erscheint :
LCD Test ist OK

Die Variable enc_delta wird leider nicht ausgegeben :o(

Was mache ich falsch ?
Liegt es an der Frequenz ?

Gruß
Gast #1199888
Lesenswert?

>  lcd_puts(small_font,enc_delta);

kann mit Sicherheit keine Interwerte ausgeben, sondern nur Strings.

Probiers mal mit
1
#include <stdlib.h>
2
...
3
char outstring[20];
4
itoa(enc_delta, outstring, 10);
5
lcd_puts(small_font,outstring);

Oliver
Gast #1199901
Lesenswert?

...wenn da jetzt nicht 2 fässer auf einmal aufgemacht werden :)

du musst doch nur detektieren, ob 2 pins am uC unterschiedlich auf masse 
gezogen werden, erst a dann b -> lass led1 leuchten / erst b dann a -> 
lass led2 leuchten...sowas kann man mit nem flipflop sogar analog lösen 
:)

also lass erstmal das lcd weg und versuche, die LOGIKAUSWERTUNG sauber 
hinzubekommen - sonst sind es auf einmal exponentiell mehr 
fehlerquellen!!!

und ich wollte nicht "böse" sein, aber es schien, dass du nichtmals 
VERSCHT hast, das datenblatt und dessen timingdiagramme zu 
interpretieren!

Klaus.
Gast #1199903
Lesenswert?

Ok Oliver.

Ich dachte beim volatile char  enc_delta; handelt es sich um eine 
character-Variable. Ups...

Aber auch das habe ich schon bereits ausprobiert...

...
for(;;)
{
lcd_clear();
char outstring[20];
itoa(enc_delta,outstring,10); //convertieren ins 10-er System
lcd_set_cursor(0,LINE1);
lcd_puts(small_font,outstring);
_delay_ms(200);
}
..

AUSGABE  auf dem LCD:

Werte zwischen 1 und 255 die sich selbständig (unsinnig) ändern ???

was soll das ganze?
#1199915
Lesenswert?

Sven B. wrote:

> Werte zwischen 1 und 255 die sich selbständig (unsinnig) ändern ???
>
> was soll das ganze?

Tja. dann wird sich wohl dein enc_delta ständig ändern.

Hast du jetzt eigentlich die externen Pullup Widerstände noch dran?
Wenn nein: Ich seh in deinem Code nirgends, wo du die internen 
einschaltest.
#1199917
Lesenswert?

Sven B. wrote:
> Und noch was.
> Wenn ich den Drehencoder ganz herausnehme, tut sich immernoch
> das gleiche auf dem LCD.
> Werte zwischen 1 und 255 sporadisch springen um.

In dem Fall ist dann alles klar.
Ein Eingang, den du offen lässt, fängt sich alle mögliche 
elektromagnetische Strahlung aus der Umgebung ein. -> interne Pullup 
Widerstände einschalten hilft ungemein.
Gast #1199919
Lesenswert?

Sven B. wrote:

> Ich dachte beim volatile char  enc_delta; handelt es sich um eine
> character-Variable. Ups...

Dann waere es immer noch falsch. Dir fehlen offensichtlich die 
C-Grundlagen, die solltest du dir erst aneignen und dich dann an 
Mikrocontroller wagen.

> AUSGABE  auf dem LCD:
>
> Werte zwischen 1 und 255 die sich selbständig (unsinnig) ändern ???

Laut deinem Schaltplan haengt der Drehgeber an D0-D2, abgefragt wird B0 
& B1, richtig initialisiert (Portrichtung & Pullup) wird keins von 
beiden.

> was soll das ganze?

Das frage ich mich auch...
Gast #1199931
Lesenswert?

Hallo Peter.

Danke für die Antwort.

Eingänge definiert.
Interne Pullup-Widerstände sind an.


int main( void )
{
  lcd_init();
  lcd_clear();
  lcd_set_cursor(0,LINE0);
  lcd_puts(small_font,"LCD Test ist OK");

  DDRB  &= ~(_BV(PB1) | _BV(PB0)); //eingang
  PORTB =  (_BV(PB1) | _BV(PB0));  //pullup

  TCCR0 = 1<<CS01;      //divide by 8 * 256
  TIMSK = 1<<TOIE0;      //enable timer interrupt

  sei();
  for(;;)
  {
   lcd_clear();
   char outstring[20];
   itoa(enc_delta,outstring,10); //convertieren ins 10-er System
   lcd_set_cursor(0,LINE1);
   lcd_puts(small_font,outstring);
  _delay_ms(200);
  }
}

Ergebnis:
Sieht gut aus!

Peter Du bist der Beste!
Ich danke Dir vielmals!
Mal wieder habt Ihr mir sehr geholfen :o)

Gruß ( freu )
Moderator Persönliche Seite #1199942
Lesenswert?

Hast du denn gar kein Stück Messmittel in Reichweite, das irgendwie
zu einer zeitlichen Darstellung zweier Signale taugt?

Hier wäre ein schneller Hack:
1
#include <stdbool.h>
2
#include <stdint.h>
3

4
#define F_CPU 16000000ul
5

6
#include <avr/io.h>
7
#include <avr/interrupt.h>
8

9
static void ioinit(void)
10
{
11
    TCCR0 = (1<<CS01) | (1<<CS00); //divide by 64 * 256 => ~ 1 ms clock ticks
12
    TIMSK = 1<<TOIE0;      //enable timer overflow interrupt
13

14
#define BAUD 9600
15
#include <util/setbaud.h>
16
    UBRRH = UBRRH_VALUE;
17
    UBRRL = UBRRL_VALUE;
18
#if USE_2X
19
    UCSRA |= (1 << U2X);
20
#else
21
    UCSRA &= ~(1 << U2X);
22
#endif
23

24
    UCSRB = (1 << TXEN);
25

26
    sei();
27
}
28

29
static void tx_char(char c)
30
{
31
    while ((UCSRA & (1 << UDRE)) == 0)
32
        /* wait */;
33
    UDR = (uint8_t)c;
34
}
35

36

37
volatile static bool updated;
38
volatile static uint8_t data;
39

40
int main( void )
41
{
42
    ioinit();
43

44
    for(;;)
45
    {
46
        if (updated)
47
        {
48
            if (data & 2) tx_char('1');
49
            else tx_char('0');
50
            if (data & 1) tx_char('1');
51
            else tx_char('0');
52
            tx_char(' ');
53
            updated = false;
54
        }
55
    }
56

57
    return 0;
58
}
59

60

61
ISR(TIMER0_OVF_vect)
62
{
63
    if (updated)
64
        return;                 /* not yet processed */
65

66
    static uint8_t lastval;
67
    uint8_t thisval = PINB;
68

69
    thisval &= 3;               /* only PINB0 & PINB1 to consider */
70
    if (thisval != lastval)
71
    {
72
        lastval = thisval;
73
        data = thisval;
74
        updated = true;
75
    }
76
}

Der sollte auf der UART Zeichenfolgen 00 01 11 10 usw. ausgeben.

Deine Timerinterruptrate von 1/(128 µs) halte ich für ziemlich
heftig.  Üblich sind Raten von 200 Hz ... 1 kHz, um einen Drehgeber
zu pollen.  Das wird natürlich nur dann ein Problem, wenn dein
Geber mehr prellt als meiner, den ich da oben genannt habe.
Gast #1199948
Lesenswert?

Hallo Jörg Wunsch!

Was zum Messen habe ich leider nicht da. kann mir von meinem 
Ausbildungsgeld kein Ossi kaufen. Könnte mir von der Arbeit ein Ossi 
mitbringen, aber die Leute haben Angst daß ich das kaputt mache.

Das mit dem UART ist ja eine tolle Idee. Danke für den Tip und Code.
Damit werde ich mich auch ein bißchen anfreunden ;o)

Ich danke Euch allen nochmal und wünsche ein schönes WE !

Sven
#1204218
Lesenswert?

Gast aus Prenzlau wrote:
> Guten Tag.
>
> Würde es gehen mit dem Code von Sven B. bis 1000 zu drehen und nicht bis
> 255 ?

Die Limitierung in seinem Code entsteht ja nur dadurch, dass er 
enc_delta als char ausgeführt hat (das sollte man sowieso besser zu 
einem signed char oder noch besser zu einem int8_t machen). Ersetz ihn 
durch einen 16-Bit Datentyp und du kannst bis 16384 zählen. Mach einen 
32 Bit Type draus und es geht noch höher.

Den Drehencoder decodieren ist eine Sache. Was du dann mit dem 
Änderungswert machst ist eine andere Sache.
Gast #1204229
Lesenswert?

Karl heinz Buchegger wrote:

> Die Limitierung in seinem Code entsteht ja nur dadurch, dass er
> enc_delta als char ausgeführt hat (das sollte man sowieso besser zu
> einem signed char oder noch besser zu einem int8_t machen). Ersetz ihn
> durch einen 16-Bit Datentyp und du kannst bis 16384 zählen. Mach einen
> 32 Bit Type draus und es geht noch höher.

Dann aber nicht vergessen, den Zugriff atomar zu machen (siehe auch 
atomic.h).
Gast #1204344
Lesenswert?

Hallo nochmal.

Sorry Karl,
es ist doch nicht so einfach...

Wenn ich die variable richtung als:

volatile unit8_t richtung=0;

definiere, kommt leider keine Änderung (0...255).

Habe ich es doch nicht verstanden ?
1
#include <avr/io.h>
2
#include "glcd.h"
3
#include "font_big.h"
4
#include <avr\interrupt.h>
5

6
#define Schalter_A PINB0 // Schalter A im Drehgeber 
7
#define Schalter_B PINB1 // Schalter B im Drehgeber
8
#define Geber      PINB  // Drehgeber Port
9

10
volatile unsigned char richtung = 0;  // 0 ... 255
11
volatile char outstring_alt[5];
12

13
int main (void)
14
{
15
  char outstring[5];
16

17
    DDRB  &= ~(_BV(PB1) | _BV(PB0));  //Eingänge
18
    PORTB =   (_BV(PB1) | _BV(PB0));  //Pullup
19

20
    TCCR0 = (1<<CS01) | (1<<CS00);    //divide by 64 * 256
21
    TIMSK = 1<<TOIE0;
22

23
  lcd_init();
24
  lcd_clear();  
25

26
  sei();  
27
  for(;;)
28
  {
29
    
30
    strcpy(outstring_alt,outstring);
31
    itoa(richtung, outstring, 10);
32

33
    if(strcmp(outstring,outstring_alt)!=0)
34
    {
35
      lcd_set_cursor(0,LINE3);lcd_puts(big_font,outstring_alt,WHITE);
36
    }
37
    lcd_set_cursor(0,LINE3);lcd_puts(big_font,outstring,BLACK);
38
  
39
  }  
40
  
41
}
42

43

44
ISR(TIMER0_OVF_vect)
45
{
46
  static unsigned char alter_status = 0,step = 0;
47
  unsigned char        neuer_status;
48

49
  neuer_status = Geber & (_BV(Schalter_A) | _BV(Schalter_B)); // änderung einlesen
50

51
  if ((neuer_status ^ step)==(_BV(Schalter_A) | _BV(Schalter_B)))
52
  {
53
    if ((neuer_status ^ alter_status)==_BV(Schalter_A))
54
      richtung +=1;                  // Es war nach rechts
55
    else
56
      richtung -=1;                  // Es war nach links
57
    step = neuer_status;
58
  }
59
  alter_status = neuer_status;
60
  
61

62
}
#1204371
Lesenswert?

Gast aus Prenzlau wrote:
> Ändert sich auch nicht
>
> Der Wert bleibt immer zwischen 0 und 255.


Ja?
Was erwartest du?

Ein uint8_t, wie der Name schon sagt, ist ein unsigned (also ohne 
Vorzeichen) int mit 8 Bit.
Mit 8 Bit kann man ohne Vorzeichen von 0 bis 255 zählen.

Wie wärs mit einem uint16_t
Der hat, wie der Name schon sagt, 16 Bit

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