Forum: Mikrocontroller und Digitale Elektronik Drehencoder für Anfänger


von Sven B. (Gast)


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

von Klaus2 (Gast)


Lesenswert?

...willst du uns veräppeln? Schau mal ganz genau ins Datenblatt vom 
großen C, Seite 4!!!

Klaus.

von Peter D. (peda)


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

von Sven B. (Gast)


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

von Sven B. (Gast)


Angehängte Dateien:

Lesenswert?

Der Anhang...

von Oliver (Gast)


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

von Oliver (Gast)


Lesenswert?

Nachtrag: Das mit deine Widerständen hätte doch gepasst, trotzdem: Lass 
auch den Schalter nach Masse schalten. Das ist einfacher.

Oliver

von Sven B. (Gast)


Angehängte Dateien:

Lesenswert?

Danke Oliver.

Habe ich Dich richtig verstanden ? Meinstest Du diese Beschaltung ?
Werden die Taster(im Drehencoder) also softwaremäßg entprellt ?
Verstehe ich das richtig ?

Gruß

von Oliver (Gast)


Lesenswert?

So ist es.

Oliver

von Klaus2 (Gast)


Lesenswert?

...zumal das alles auch im Datenblatt steht - aber wer nicht nachschaut, 
findet das auch nicht.

Klaus.

von Sven B. (Gast)


Lesenswert?

Vielen Dank für Eure Hilfe.

Gruß

von Sven B. (Gast)


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ß

von Sven B. (Gast)


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ß

von Oliver (Gast)


Lesenswert?


von Sven B. (Gast)


Lesenswert?

OK.
Danke Oliver.

Ich Probiere das mal aus und melde mich gleich wieder.
Bin sehr gespannt.

Danke erstmal

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


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.

von Sven B. (Gast)


Lesenswert?

OK.

Ich denke die beste Methode wird sein, wenn ich das ganze mal anstatt 
als signal an einer LED -  auf einem LCD ausgebe.

Ich krame das mal kurz raus und melde mich gleich wieder.

danke erstmal für die tips.

von Sven B. (Gast)


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ß

von Oliver (Gast)


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

von Klaus2 (Gast)


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.

von Sven B. (Gast)


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?

von Sven B. (Gast)


Lesenswert?

P.S.
Das Drehen des Drehencoders(links oder rechts) wirkt sich nicht auf die 
Ausgabe aus.

D.h. Das Ding funktioniert nicht.

von Sven B. (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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.

von P. S. (Gast)


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...

von Sven B. (Gast)


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 )

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


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.

von Sven B. (Gast)


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

von Philipp (Gast)


Lesenswert?

> kann mir von meinem Ausbildungsgeld kein Ossi kaufen.

Nenn's bitte Oszi oder Scope. Oder gleich ganz.

Mit "Ossi" meinst du abfällig eine ganze Bevölkerungsgruppe ;-)

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


Lesenswert?

"Ossi" wäre ja dann wohl auch die Kurzform für "Ossiloskop". :-)

von Sven B. (Gast)


Lesenswert?

Ja ihr habt Recht!
Sorry ich meinte auch Osziloskop :o)

Wollte niemanden beleidigen !

von Stephan H. (stephan-)


Lesenswert?

@Jörg,

welche Bevölkerungsschicht hat Dir denn vorgeschwebt ??
Was ist ein "Ossi" ??  :-(

von Gast aus Prenzlau (Gast)


Lesenswert?

Guten Tag.

Würde es gehen mit dem Code von Sven B. bis 1000 zu drehen und nicht bis 
255 ?

von Karl H. (kbuchegg)


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.

von Gast aus Prenzlau (Gast)


Lesenswert?

Danke für die Antwort.

Das ist ja einfacher als ich dachte.

von P. S. (Gast)


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).

von Route_66 (Gast)


Lesenswert?

@Karl heinz Buchegger
16 Bit geht von 0...65535 oder -32768...32767.

von Karl H. (kbuchegg)


Lesenswert?

Route_66 wrote:
> @Karl heinz Buchegger
> 16 Bit geht von 0...65535 oder -32768...32767.

Kann nur sagen: hatte heute noch keinen Kaffee und draussen schneits und 
überhaupt :-)

Danke

von Gast aus Prenzlau (Gast)


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
}

von Sven B. (Gast)


Lesenswert?

Bei volatile unit8_t richtung=0;

gibt es sowieso eine Fehlermeldung :o)

Versuch mit

...
volatile uint8_t richtung=0;
...

Gruß

von Karl H. (kbuchegg)


Lesenswert?

Code den man nicht versteht einfach nur so zusammenzukopieren, hat nocht 
nie funktioniert.

von Gast aus Prenzlau (Gast)


Lesenswert?

Ändert sich auch nicht

Der Wert bleibt immer zwischen 0 und 255.

von Karl H. (kbuchegg)


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

von Sven B. (Gast)


Lesenswert?

Richtig.

volatile uint16_t richtung=0;

....
if (richtung>=1000) richtung=0;
...

Vorsicht,daß Du keinen Krampf beim Drehen bekommst...

Sven

von Mich mag keiner (Gast)


Lesenswert?

schneits -> schneit's

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.