www.mikrocontroller.net

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


Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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)
// target: ATmega8
//------------------------------------------------------------------------
 
#ifndef F_CPU
#warning "F_CPU was not defined yet, now make up with 4000000"
#define F_CPU 4000000L // Systemtakt in Hz
#endif

#include <avr/io.h>
#include <avr/interrupt.h>
#include "Drehgeber_usart.h"
 
#define PHASE_A    (PINB & 0x01)
#define PHASE_B    (PINB & 0x02)
 
#define LEDS_DDR  DDRC
#define  LEDS    PORTC      // LEDs against VCC
 
 //defineglobal variables
static volatile int16_t enc_delta;      // -128 ... 127
static int ADIteiler=0;
static char s[20];



void encode_init( void )
{
  DDRB = 0x00;     // define as Input
  PORTB |=0x03;    //enables Pull-Up on Port B
  enc_delta = 0;
  
  //Timer Compare 2
 
  TCCR2 |= (1<<WGM21);  //CTC
  TCCR2 |= (1<<CS22);  //--> CPU Takt --> 4MHz/64 = 62500 --> 16 us
  OCR2  |= (uint8_t) (F_CPU / 64.0 * 1e-3 - 0.5);  // Vergleichsregister 
  TIMSK |= (1<<OCIE2);  //Enable Timer/counter2 Compare Match Interrupt
}
 
 
ISR( TIMER2_COMP_vect )  // for manual movement
{
  ADIteiler++;
  if(ADIteiler >= 244)  //Counter cycle for all 4,1 ms * 60 = 0,24 s
  {
    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

    USART_Puts("\r\n\n");
  utoa( enc_delta, s, 10 );
  USART_Puts( s );  
   }
  }
  
}

int main( void )
{ 
  DDRD = 0xff;
  USART_Init();

  USART_Puts("\r\n");
  USART_Puts("Messung wird gestartet:");
  USART_Puts("\r\n");
 
  LEDS_DDR = 0xFF;
  encode_init();
  sei();
 
  for(;;)
    LEDS = enc_delta;
}

MFG
Danke

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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:
    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.

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.
ISR( TIMER2_COMP_vect )  // for manual movement
{
  ...

  now = PINB & ( ( 1 << PB0 ) | ( 1 << PB1 ) );  // es interessieren nur
                                                 // die Bits PB0 und PB1

  if( now != old )  // nur wenn eine Veränderung fetsstellbar ist
  {
    ...


    old = now;     // jetziger Zustand ist für die nächste Abfrage der
                   // vorhergehende Zustand
  }
}

Durch geeignete Umformungen landet man dann irgendwann bei Peters Code.

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

    // target: ATmega8
//------------------------------------------------------------------------
 
#ifndef F_CPU
#warning "F_CPU was not defined yet, now make up with 4000000"
#define F_CPU 4000000L      // Systemtakt in Hz 
#endif

#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include "Drehgeber_usart.h"
 
#define PHASE_A    (PINB & 0x01)
#define PHASE_B    (PINB & 0x02)
 
#define LEDS_DDR  DDRC
#define  LEDS    PORTC    // LEDs against VCC
 
 //defineglobal variables
static volatile int8_t enc_delta;      // -128 ... 127
static int8_t last;
static char s[20];

void encode_init( void )
{
  int8_t neww;
  DDRB = 0x00;        // define as Input
  PORTB |=0x03;          //enables Pull-Up on Port B
 
  neww = 0;
  if( PHASE_A )
    neww = 3;
  if( PHASE_B )
    neww ^= 1;        // convert gray to binary
  last = neww;        // power on state
  enc_delta = 0;
  
  //Timer compare 2  
  TCCR2 |= (1<<WGM21);  //CTC
  TCCR2 |= (1<<CS22);  //--> CPU Takt --> 4MHz/64 = 62500 --> 16 us
  OCR2  |= (uint8_t) (F_CPU / 64.0 * 1e-3 - 0.5);  // Vergleichsregister 
  TIMSK |= (1<<OCIE2);  //Enable Timer/counter2 Compare Match Interrupt
}
 
 
ISR( TIMER2_COMP_vect )        // 1ms for manual movement
{
  int8_t neww, diff;
 
  neww = 0;
  
  if( PHASE_A )
    neww = 3;
  if( PHASE_B )
    neww ^= 1;        // convert gray to binary
  diff = last - neww;      // difference last - new
  if( diff & 1 )      // bit 0 = value (1)
  {          
    last = neww;       // store new as next last
    enc_delta += (diff & 2) - 1;   // bit 1 = direction (+/-)
  }
}

int8_t encode_read1( void )       // read single step encoders
{
  int8_t val;

  cli();
  val = enc_delta;
  enc_delta = 0;
  sei();
  
  return val;      // counts since last call
}


int main( void )
{
  int32_t value = 0;
  DDRD = 0xff;

  USART_Init();

  USART_Puts("\r\n");
  USART_Puts("Messung wird gestartet:");
  USART_Puts("\r\n");
 
  LEDS_DDR = 0xFF;
  encode_init();
  sei();
 
  for(;;){
    
  value += encode_read1();    // read a single step encoder
  cli();
  USART_Puts("\r\n\n");
  utoa( value, s, 10 );   
  USART_Puts( s );
    LEDS = value;
  }
}

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!!!!!!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: gast (Gast)
Datum:

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

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
oh entschuldigung
Frequenz = 16 Mhz

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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  :-/

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 :-/

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.