Forum: Mikrocontroller und Digitale Elektronik drehgeber portB wird nie gleich 1


von gast (Gast)


Lesenswert?

Hallo,
Ich glaube, ich habe da ein Problem und stehe auf dem Schlauch.....

Alsoooo...

ich beschäftige mich gerade mit Drehgebern und deren Auswertung mit
einem µC. Ich bin allerdings gerade etwas verwirrt denn in
verschiedenen Quellen

Beitrag "Drehgeber auslesen"
http://www.mikrocontroller.net/articles/Drehgeber
und den Tutoriell über Drehgeber von Peter, verstehe ich nur Bahnhof vom 
Code.
Ich habe sogar auf Papier versuch die Logik zu verstehe, leider nichts.

Mit meinem Code kann der Drehgeber nur dekrementieren B Chanel wird nie 
auf 1 gesetzt, Natürlich habe ich beide Ausgänge ausgeführt und auf dem 
Oszilloskop geschlossen, dort ist die Pegel und Flanke Wechselung 
richtig angezeigt.

Ich komme einfach nicht weiter.
kann mir jemand weiter helfen?

Hier unter meine Drehgeber Auswertung Überlegung und dann die Code, die 
ich mir ausgedacht habe.


Rechst (Vorwärts)
Gray-Code   -  Binär-Code
11 -   -   -   10 = 01 (1)
01 -    -   -  00 = 01 (1)
00 -   -   -   11 = 01 (1)
10 -   -   -   01 = 01 (1)


Links (Rückwärts)
Gray-Code   -  Binär-Code
01 -   -   -   10 = 11 (3)
11 -   -   -   00 = 11 (3)
10 -   -   -   11 = 11 (3)
00 -   -   -   01 = 11 (3)
1
// target: ATmega8
2
//------------------------------------------------------------------------
3
 
4
#ifndef F_CPU
5
#warning "F_CPU was not defined yet, now make up with 4000000"
6
#define F_CPU 4000000L // Systemtakt in Hz
7
#endif
8
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
#include "Drehgeber_usart.h"
12
 
13
#define PHASE_A    (PINB & 0x01)
14
#define PHASE_B    (PINB & 0x02)
15
 
16
#define LEDS_DDR  DDRC
17
#define  LEDS    PORTC      // LEDs against VCC
18
 
19
 //defineglobal variables
20
static volatile int16_t enc_delta;      // -128 ... 127
21
static int ADIteiler=0;
22
static char s[20];
23
24
25
26
void encode_init( void )
27
{
28
  DDRB = 0x00;     // define as Input
29
  PORTB |=0x03;    //enables Pull-Up on Port B
30
  enc_delta = 0;
31
  
32
  //Timer Compare 2
33
 
34
  TCCR2 |= (1<<WGM21);  //CTC
35
  TCCR2 |= (1<<CS22);  //--> CPU Takt --> 4MHz/64 = 62500 --> 16 us
36
  OCR2  |= (uint8_t) (F_CPU / 64.0 * 1e-3 - 0.5);  // Vergleichsregister 
37
  TIMSK |= (1<<OCIE2);  //Enable Timer/counter2 Compare Match Interrupt
38
}
39
 
40
 
41
ISR( TIMER2_COMP_vect )  // for manual movement
42
{
43
  ADIteiler++;
44
  if(ADIteiler >= 244)  //Counter cycle for all 4,1 ms * 60 = 0,24 s
45
  {
46
    if  ((PINB&(1<<PB0))==0) //Wenn A auf 0 geht (R oder L drehen)
47
   {
48
     if ((PINB&(1<<PB1))==1) enc_delta++; //Wenn A=0 und B=1 inc
49
     else if ((PINB&(1<<PB1))==0) enc_delta--; //Wenn A=0 und B=0 dec
50
51
    USART_Puts("\r\n\n");
52
  utoa( enc_delta, s, 10 );
53
  USART_Puts( s );  
54
   }
55
  }
56
  
57
}
58
59
int main( void )
60
{ 
61
  DDRD = 0xff;
62
  USART_Init();
63
64
  USART_Puts("\r\n");
65
  USART_Puts("Messung wird gestartet:");
66
  USART_Puts("\r\n");
67
 
68
  LEDS_DDR = 0xFF;
69
  encode_init();
70
  sei();
71
 
72
  for(;;)
73
    LEDS = enc_delta;
74
}

MFG
Danke

von Stefan E. (sternst)


Lesenswert?

1
 if ((PINB&(1<<PB1))==1) enc_delta++;
(PINB&(1<<PB1)) ist entweder 0 oder 2, aber niemals 1.
Entweder du schreibst !=0, oder lass den Vergleich einfach ganz weg.

PS: Erwarte aber nicht, dass der Code als Ganzes dann geht. Da sind noch 
andere Probleme in der grundsätzlichen Logik.

von Karl H. (kbuchegg)


Lesenswert?

Stefan Ernst schrieb:

> PS: Erwarte aber nicht, dass der Code als Ganzes dann geht. Da sind noch
> andere Probleme in der grundsätzlichen Logik.

zb hier:
1
    if  ((PINB&(1<<PB0))==0) //Wenn A auf 0 geht (R oder L drehen)

das testet nicht, ob A von 1 auf 0 gewechselt hat.
Das testet, ob A gleich 0 ist. Das ist aber etwas völlig anderes. A 
kann eine halbe Stunde auf 0 stehen, wenn zb niemand am Encoder dreht.

von gast (Gast)


Lesenswert?

danke erst mal

>PS: Erwarte aber nicht, dass der Code als Ganzes dann geht. Da sind noch
>andere Probleme in der grundsätzlichen Logik.

Genau diese Logik versuch ich seit ewig zu verstehen.

Mit deinem Vorschlag, kann ich jetzt inkrementieren, aber nicht mehr 
dekrementieren. Und sogar beim Abbruch Drehgeber zu drehen, zähl er 
weiter.

Das verstehe ich schon, weil wenn beim Abbruch die Phase immer auf 0 
dann zähl er weiter.

Wie kann ich dann diese Logik vom Drehgeber am besten programmieren!?!?

Rechst (Vorwärts)
Gray-Code   -  Binär-Code
11 -   -   -   10 = 01 (1)
01 -    -   -  00 = 01 (1)
00 -   -   -   11 = 01 (1)
10 -   -   -   01 = 01 (1)


Links (Rückwärts)
Gray-Code   -  Binär-Code
01 -   -   -   10 = 11 (3)
11 -   -   -   00 = 11 (3)
10 -   -   -   11 = 11 (3)
00 -   -   -   01 = 11 (3)

 Danke

von P. S. (Gast)


Lesenswert?

Wie waere es erst einmal mit einer einfachen, aber uebersichtlichen 
Variante?

    if  ((PINB&(1<<PB0))==0) //Wenn A auf 0 geht (R oder L drehen)
   {
     if ((PINB&(1<<PB1))==1) enc_delta++; //Wenn A=0 und B=1 inc
     else if ((PINB&(1<<PB1))==0) enc_delta--; //Wenn A=0 und B=0 dec

Wenn ich sowas schon sehe - hat man es als Anfaenger nicht schon schwer 
genug, als dass man sich noch mit Unuebersichtlichkeit quaelen muesste?

Ein Drehgeber funktioniert doch ganz einfach, du hast zwei Pins, die 
geben zusammen einen Wert. Wenn sich der Wert aendert, ist der Gegeber 
bewegt worden. Die Richtung haengt davon ab, wie der Wert vorher war und 
wie er jetzt ist. Das steht in den Tabellen oben. Also machst du erst 
mal primitiv einen dicke if-else-Baum. Wenn du das hast und es 
funktioniert, baust du es auf eine Tabelle um. Und wenn das geht, kannst 
du dich ja an die xor-Variante machen.

von Karl H. (kbuchegg)


Lesenswert?

Peter Stegemann schrieb:

> Wenn sich der Wert aendert, ist der Gegeber
> bewegt worden.

Ich denke, hier liegt der erste Knackpunkt:

Wenn sich der Wert ändert.

Also muss als erstes eine Auswertung her, die feststellt ob sich der 
Wert überhaupt verändert hat.
Das geht aber nicht, indem man nur den momentanen Zustand der Pins 
betrachtet. Eine Veränderung kann man nur feststellen, wenn man einen 
Zustand von vorher mit dem jetzigen Zustand vergleicht.
1
ISR( TIMER2_COMP_vect )  // for manual movement
2
{
3
  ...
4
5
  now = PINB & ( ( 1 << PB0 ) | ( 1 << PB1 ) );  // es interessieren nur
6
                                                 // die Bits PB0 und PB1
7
8
  if( now != old )  // nur wenn eine Veränderung fetsstellbar ist
9
  {
10
    ...
11
12
13
    old = now;     // jetziger Zustand ist für die nächste Abfrage der
14
                   // vorhergehende Zustand
15
  }
16
}

Durch geeignete Umformungen landet man dann irgendwann bei Peters Code.

von gast (Gast)


Lesenswert?

nach vielen Überlegungen bin ich auf dieselbe Variante wie peter 
gekommen.
für mich wäre es die Optimal Lösung für meinen Fall.
Leider kriege ich manchmal den Wert inkrementiert oder dekrementiert 
unabhängig vom Drehrichtung.
soll meine Frequenz bestimmt sein.

ich habe 1000 Impulse pro Umdrehungen und dreh Manuel mit dem Hand.

Kann jemand mir mein Fehler zeigen?!.

MFG
Danke
1
    // target: ATmega8
2
//------------------------------------------------------------------------
3
 
4
#ifndef F_CPU
5
#warning "F_CPU was not defined yet, now make up with 4000000"
6
#define F_CPU 4000000L      // Systemtakt in Hz 
7
#endif
8
9
#include <avr/io.h>
10
#include <stdlib.h>
11
#include <avr/interrupt.h>
12
#include "Drehgeber_usart.h"
13
 
14
#define PHASE_A    (PINB & 0x01)
15
#define PHASE_B    (PINB & 0x02)
16
 
17
#define LEDS_DDR  DDRC
18
#define  LEDS    PORTC    // LEDs against VCC
19
 
20
 //defineglobal variables
21
static volatile int8_t enc_delta;      // -128 ... 127
22
static int8_t last;
23
static char s[20];
24
25
void encode_init( void )
26
{
27
  int8_t neww;
28
  DDRB = 0x00;        // define as Input
29
  PORTB |=0x03;          //enables Pull-Up on Port B
30
 
31
  neww = 0;
32
  if( PHASE_A )
33
    neww = 3;
34
  if( PHASE_B )
35
    neww ^= 1;        // convert gray to binary
36
  last = neww;        // power on state
37
  enc_delta = 0;
38
  
39
  //Timer compare 2  
40
  TCCR2 |= (1<<WGM21);  //CTC
41
  TCCR2 |= (1<<CS22);  //--> CPU Takt --> 4MHz/64 = 62500 --> 16 us
42
  OCR2  |= (uint8_t) (F_CPU / 64.0 * 1e-3 - 0.5);  // Vergleichsregister 
43
  TIMSK |= (1<<OCIE2);  //Enable Timer/counter2 Compare Match Interrupt
44
}
45
 
46
 
47
ISR( TIMER2_COMP_vect )        // 1ms for manual movement
48
{
49
  int8_t neww, diff;
50
 
51
  neww = 0;
52
  
53
  if( PHASE_A )
54
    neww = 3;
55
  if( PHASE_B )
56
    neww ^= 1;        // convert gray to binary
57
  diff = last - neww;      // difference last - new
58
  if( diff & 1 )      // bit 0 = value (1)
59
  {          
60
    last = neww;       // store new as next last
61
    enc_delta += (diff & 2) - 1;   // bit 1 = direction (+/-)
62
  }
63
}
64
65
int8_t encode_read1( void )       // read single step encoders
66
{
67
  int8_t val;
68
69
  cli();
70
  val = enc_delta;
71
  enc_delta = 0;
72
  sei();
73
  
74
  return val;      // counts since last call
75
}
76
77
78
int main( void )
79
{
80
  int32_t value = 0;
81
  DDRD = 0xff;
82
83
  USART_Init();
84
85
  USART_Puts("\r\n");
86
  USART_Puts("Messung wird gestartet:");
87
  USART_Puts("\r\n");
88
 
89
  LEDS_DDR = 0xFF;
90
  encode_init();
91
  sei();
92
 
93
  for(;;){
94
    
95
  value += encode_read1();    // read a single step encoder
96
  cli();
97
  USART_Puts("\r\n\n");
98
  utoa( value, s, 10 );   
99
  USART_Puts( s );
100
    LEDS = value;
101
  }
102
}

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> ISR( TIMER2_COMP_vect )        // 1ms for manual movement
Diese eine ms ist OK für Geber mit 16 oder 32 Rastungen pro Umdrehung.
Bei 1000 Impulsen pro Umdrehung bist du hier viel zu langsam.

von gast (Gast)


Lesenswert?

wie berechnet man das !?!?!
wie komme ich drauf dass für 1000 Impulsen pro Umdrehung ich eine andere 
frequenz brauche.
Habe gerade mit 16 Hz probiert jetzt habe ich probleme mit meinem 
UART!!!!!!

von Karl H. (kbuchegg)


Lesenswert?

gast schrieb:
> wie berechnet man das !?!?!

gar nicht. Sondern man denkt ein wenig nach.

> wie komme ich drauf dass für 1000 Impulsen pro Umdrehung ich eine andere
> frequenz brauche.

Du schätzt wie lange es wohl dauert mit der Hand den Encoder einmal zu 
drehen.
Bei einer Umdrehung verändert der Encoder 1000 mal seine Stellung. Aus 
deiner Schätzung weißt du daher auch wie lange es dauert, bis der 
Encoder einmal seine Stellung wechselt. Und dann denkst du daran, dass 
du mindestens 2-mal in dieser Zeit nachsehen solltest, ob sich der 
Encoder verändert hat.
Und dann nimmst du das alte Motto der Technik her: Grau lieber Freund 
ist alle Theorie, schlag lieber noch einen Faktor 3 zur Sicherheit 
drauf.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> Habe gerade mit 16 Hz probiert
Welche 16 Hz denn?
Ich kann mich dem Eindruck nicht verwehren, dass du im Dunkeln 
stocherst...

> Leider kriege ich manchmal den Wert inkrementiert oder dekrementiert
> unabhängig vom Drehrichtung.
Was passiert, wenn du gaaaaaanz laaaangsaaam drehst?

von gast (Gast)


Lesenswert?

> Was passiert, wenn du gaaaaaanz laaaangsaaam drehst?
es geht
aber wenn bei schneller Drehung nicht

von gast (Gast)


Lesenswert?

oh entschuldigung
Frequenz = 16 Mhz

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

>> Was passiert, wenn du gaaaaaanz laaaangsaaam drehst?
> es geht
> aber wenn bei schneller Drehung nicht
Also, dann siehst du jetzt mal zu, dass der 1 ms Timer-Interrupt 
wesentlich öfter kommt. Als Gedankengrundlage nimmst du das, was Karl 
heinz Buchegger schon schön beschrieben hat.

Z.B. 1 U/s per Hand macht 1000 Impulse/sec, bei 4-fach Auswertung und 
mindestens doppelter Abtastrate sind das dann 8000 Hz. Dazu die 3-fache 
Sicherheit gibt  ca. 25kHz.
Fazit: stell den Timer auf eine Wiederholzeit von ca. 40 us ein...

Ob dir die übrige Rechenleistung dann noch ausreicht, das steht auf 
einem anderen Blatt  :-/

von P. S. (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:

> Und dann nimmst du das alte Motto der Technik her: Grau lieber Freund
> ist alle Theorie, schlag lieber noch einen Faktor 3 zur Sicherheit
> drauf.

Und wenn ein boshafter Benutzer den Knopf "anschnippt", hast du immer 
noch zu wenig :-/

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.