Go Down

Topic: Verhalten Rotary Encoder (Read 10329 times) previous topic - next topic

currymuetze

Jan 09, 2012, 11:51 pm Last Edit: Jan 09, 2012, 11:53 pm by currymuetze Reason: 1
Hi,
habe mal eben mit meinen Encodern rumgetestet:
Specifications:

Rotary Shaft Length: Approx. 12mm
Shaft Full Length: Approx. 20mm
Shaft Diameter: Approx. 6mm
Size (L x W): Approx. 15 x 11mm
Position: 20
Pulse: 20
Output: 2-bit gray code
Closed Circuit Resistance: 3 ohms maximums
Max. Rating: 10 mA @ 5 VDC
Operating Temperature Range: -30 to 70 degrees celsius
Storage Temperature Range: -40 to 85 degrees celsius
Rotational Life: 30000 cycles minimum
Switch Life: 20000 cycles minimum

Hier zunächst mein TestCode:
Code: [Select]

/******Encoder*************/
int AAread=0;
int BBread=0;
byte modus=0;
byte A=3;
byte B=2;
byte modusa=0;
byte buttonA=4;
int d;
boolean once=false;

/************SETUP*****************/
void setup()
{
 Serial.begin(115200);
/************ENCODER*************/  
 pinMode(buttonA, INPUT);

 pinMode(A,INPUT);                        
 pinMode(B,INPUT);

 digitalWrite(buttonA, HIGH);  

 digitalWrite(A,HIGH);                          
 digitalWrite(B,HIGH);                        
 
 attachInterrupt(0,encoder,CHANGE);    
attachInterrupt(1,encoder,CHANGE);    

}
/***************HAUPTSCHLEIFE******************/
void loop()
{
 if (modusa==0) {
if (once==false) {
     once=true;
   }
    Serial.println("Modus 0 / Secs: ");
    Serial.println(millis());
    delay(1000);
   }
 if (modusa==1) {
       if (once==false) {
     once=true;
   }
    Serial.println("Modus 1 / Secs: ");
    Serial.println(millis());
  delay(1000);
 }
 if (modusa==2) {
       if (once==false) {
     once=true;
   }
    Serial.println("Modus 2 / Secs: ");
 Serial.println(millis());
delay(1000);
 }
 if (modusa==3) {
       if (once==false) {
     once=true;
   }
    Serial.println("Modus 3 / Secs: ");
 Serial.println(millis());
delay(1000);
 }
 }

/****WECHSEL MANUELL AUTOMATISCH*****/

void Button(){
 byte read1=digitalRead(buttonA);

 if (read1==HIGH && once==true) {
   ++modusa;
   once=false;
 }
 if (modusa==3) modusa=0;

}
/********Encoder Steuerung blau [1] und [2] und Mondlicht [4]*****/
void encoder() {
 AAread=digitalRead(A);
 BBread=digitalRead(B);
 /*--Linksdrehen--*/
 if (AAread==LOW && BBread==LOW ||AAread==HIGH&& BBread==HIGH){
   d=d-1;
   Serial.print("Linksdrehen: ");
   Serial.println(d,DEC);    
 }  
 /*--Rechtsdrehen--*/
 if (AAread==HIGH && BBread==LOW||AAread==LOW && BBread==HIGH) {
   d=d+1;
   Serial.print("Rechtsdrehen: ");
   Serial.println(d, DEC);    
 }  
}    



2 Fehler, bislang die Ursache trotz diverser Tests nicht gefunden:
1) Click auf Button schaltet nicht den Modus weiter. Habe ich irgendwie falsch entprellt (boolean once)?
2) Die interrupts 0 und 1 liegen auf A und B, also nur drehen. Wenn ich jetzt drehe passiert auch was, aber irgendwie wird immer mit einem Raster ein rechtsdrehen und ein linksdrehen ausgelöst. Siehe Screenshot. Was hab ich da falsch programmiert?

Eigentlich ist der Code mehr oder weniger von einem funktionierenden Code, aber ein neuer Encoder.
Angeschlossen ist folgend:
auf der 3-PIN Seite:
A und B an die Interrupts, C an GND
auf der 2-PIN Seite:
einer an Digital-In, einer an GND

Hoffe mir kann jemand helfen. Ich hatte meine alten Panasonic Encoder bereits am laufen, eigentlich mit fast dem selben Code. Daher bin ich grad verwirrt....
Gruß


EDIT an Mod:
kann leider mein 66kb Anhang nicht anhängen:
The upload folder is full. Please try a smaller file and/or contact an administrator.

uwefed

Der Graycode ist ein absoluter Coder der bei jedem Weiterzählen nur ein Bit ändert und nicht mehrere wie der Binärcode wenn er zB von 7 auf 8 wechselt (B0111 auf B1000) http://de.wikipedia.org/wiki/Graycode
Es ist kein incrementeller Code wie eine 2 Phasiges Signal einer Lochscheibe.

Aus der Beschreibung des Encoders werde ich nicht schlau. Einerseits hat er 2 Bit Graycode; somit eine Auflösung von 4 und andererseits 20 Pulse pro Umdrehung.
Hast Du den Herstellercode oder ein Datenblatt?
Grüße Uwe 

currymuetze

Hi Uwe,

das mit dem Graycode habe ich jetzt so verstanden - aber in wie fern ist das der Grund für die Verwirrung ("aber irgendwie wird immer mit einem Raster ein rechtsdrehen und ein linksdrehen ausgelöst")?
Es ging ja mal mit einem anderem Encoder. Also vermute ich jetzt, dass ich was am Code falsch habe?

Leider habe ich kein Datenblatt gefunden, die Dinger sind von Ebay, ich habe den Verkäufer mal angeschrieben.

Gruß

volvodani

Du musst ein "Datentable" hinterlegen und die beiden Eingänge vergleichen in welche Richtung gedreht hast. Der ander Code ist für einen Incrementalgeber bei dem sich die Signale anders verhalten.  :)

So sieht man seinen Code wieder :smiley-mr-green:
"Komm wir essen Opa!" - Satzzeichen retten Leben!

uwefed

Der 2 Bit Gray Code hat folgende Wahrheitstabelle:
A  B
0 0
0 1
1 1
1 0

Daraus ist ersichtlich, daß keine Richtungsinformation bei einem H-L bzw L-H Übergang wie bei einem 90 Grad versetzten Phasensignal aus dem Zustand der Signale der beiden Leitungen erhalten werden können.

Es können aber auch nur 4 Positionen pro Umdrehung erkannt werden und darum verstehe ich die Information "pulse 20" in den Daten nicht. 

Mit anderen Worten es ist der Falsche Encoder um auf diese Weise Richtungsinfo zu erhalten. Richtungsinfo erhälst Du wenn Du die Letzte Position mit der Aktuellen vergleichst und die Richtung errechnest.

Viele Grüße Uwe

erni-berni

Hallo currymuetze,
wenn es ein Graycode in dieser Form ist
PinA PinB
0      0    |   Drehrichtung cw
0      1    |
1      1    |
1      0    V
dann schau dir mal meinen Code an (Anwendung in der Menüsteuerung von mehrzeiligen LCD Displays: http://arduino.cc/forum/index.php/topic,85537.msg640573.html#msg640573)
Die entscheidenden Teile nehme ich hier mal auf. Das Prinzip funktioniert mit nur einem Interrupt, der ausgelöst wird, wenn sich der Zustand von PinA ändert. In Abhängigkeit vom angenommenen Zustand PinA und dem Zustand von PinB wird die Drehrichtung bestimmt.
Code: [Select]

#define PINA 2
#define PINB 7
#define INTERRUPT 0  // that is, pin 2
volatile boolean turned;   // rotary was turned
volatile boolean up;  // true when turned cw

// Interrupt Service Routine for a change to encoder pin A
void isr ()
{
 if (digitalRead (PINA))
   up = digitalRead (PINB);
 else
   up = !digitalRead (PINB);
 turned = true;
}  // end of isr

void setup ()
{
 digitalWrite (PINA, HIGH);     // enable pull-ups
 digitalWrite (PINB, HIGH);
 digitalWrite (PUSHP, HIGH);
 attachInterrupt (INTERRUPT, isr, CHANGE);   // interrupt 0 is pin 2
}

void loop ()
{
 if (turned)
   {
   if (up)
     [i]<hier Unterprogramm für cw-Drehung aufrufen>[/i];
   else
     [i]<hier Unterprogramm für ccw-Drehung aufrufen>[/i];
   turned = false;
   }
}

Gruß
Reinhard

Nachtrag: gerade den Beitrag von Uwe gesehen. Mein Encoder hat auch 16 Rastpositionen pro Umdrehung. D.h. der Graycode wiederholt sich, in meinem Fall 4x / Umdrehung. Es geht ja auch nicht um eine absolute Position, sondern nur darum ob nach rechts oder links gedreht wird.

currymuetze


Du musst ein "Datentable" hinterlegen und die beiden Eingänge vergleichen in welche Richtung gedreht hast. Der ander Code ist für einen Incrementalgeber bei dem sich die Signale anders verhalten.  :)

So sieht man seinen Code wieder :smiley-mr-green:

Hi,
jip genau - war mir nur nicht bvewusst, dass die Encoder so unterschiedlich sind ;) Damals hab ich ja auch den Panasonic genutzt, mir ging aber die mehr oder weniger nicht mögliche BEfestigung an einem Gehäuse auf den Geist. Also habe ich mir mal (blind) neue besorgt, die man anschrauben kann.

Werde mir mal ErniBernis COde anschauen und schauen obs klappt oder ob Uwe Recht hat.
Allerdings frage ich mich wofür der Encoder gut sein soll, wenn er nicht für diese Anwendungen funktioniert? Ich hoffe dann mal auf einen Datenblattfehler.


Gruß

currymuetze

Hallo,

so, habe mich an ErnieBernis Code gehalten, aber irgendwie komme ich nicht weiter.
Den BUtton habe ich erst mal deaktiviert. Angeschlossen ist der Encoder an 2,4 und an GND.
Frage: Ist es relevant, welche Baudrate man hier beim SerialPrint einstellt?

Hier der Code:
Code: [Select]
//**********************************************Rotary Encoder*********************************
#define PINA 2
#define PINB 4
#define PUSH1 3
#define INTERRUPTA 0  
#define INTERRUPTB 1

volatile boolean turned;   // rotary was turned
volatile boolean fired;    // knob was pushed
volatile boolean up1;  // true when turned cw

int d;
byte modus=0;

void setup ()
{
 Serial.begin(115200);
//**********************************************Rotary Encoder*********************************
 digitalWrite (PINA, HIGH);
 digitalWrite (PINB, HIGH);
 digitalWrite (PUSH1, HIGH);
 attachInterrupt (INTERRUPTA, isr1, CHANGE);
// attachInterrupt (INTERRUPTB, isrp, FALLING);
}

// Interrupt Service Routine for a change pushpin
void isrp ()
 {
 if (!digitalRead (PUSH1))
   fired = true;
 }  
 
 
// Interrupt Service Routine for a change to encoder pin A
void isr1 ()
 {
 if (digitalRead (PINA))
   up1 = digitalRead (PINB);
 else
   up1 = !digitalRead (PINB);
 turned = true;
 }  // end of isr






void loop ()
{    
 if (turned) {
   if (up1) {
     d=d-1;
     Serial.print("Linksdrehen: ");
     Serial.println(d,DEC);    
   }  
   else {
     d=d+1;
     Serial.print("Rechtsdrehen: ");
     Serial.println(d, DEC);    
     turned = false;
   }
 }
 

}

uwefed


Allerdings frage ich mich wofür der Encoder gut sein soll, wenn er nicht für diese Anwendungen funktioniert? Ich hoffe dann mal auf einen Datenblattfehler.
Gruß

Dass frage ich mich auch. Ein Encoder mit Gray-Code kann die absolute Position bestimmen in der er sich gerade befindet; Dazu braucht es aber mehr als 2 Bit; mit 4 Bit sind 16 Positionen bestimmbar, mit 5Bit sind es 32.
Der Vorteil eines Gray-Codes ist daß bei jeder Inkrementierung / Decrementierung nue 1 Bit sich ändert und so eine nicht genau eingestelte Ableseeinheit keine Bitfehler verursacht.

Grüße Uwe

currymuetze

Hi Uwe,
wie finde ich jetzt raus, ob der Encoder falsch ist (was ich mir irgendwie auch nicht vorstellen kann, leider habe ich aber kein weiteres Datenblatt)?
Habt ihr euch meinen letzten Testcode angeschaut? Meine Erwartung bei dem Code wäre: Die AUsgabe über Serial ist leer, bis ich an dem ENcoder drehe. Wenn ich drehe erscheint die Richtung mir dem Wert d.
Habe ich da irgendwo einen Fehler drin? Wenn nicht, dann kann es ja nur am Encoder liegen, aber so wie ich ErnieBernie wiederum verstanden habe halt auch nicht.

Sehr verwirrend für Einsteiger

Gruß

farbtoaster

du hast sowas: http://de.wikipedia.org/wiki/Inkrementalgeber
da interessiert nicht die Position sondern die drehbewegung an sich.

Grüsse

currymuetze

Hi Farbtoaster,
vielen Dank für den Link.

Was bedeutet das denn nun für meinen Code? Ist der falsch? Nach welchem BeispielCode kann ich mich dann mal orientieren und damit rumspielen?

Vielen Dank und Gruß
THorsten 

currymuetze

Nochmal zu meinem Verständnis:

Farbtoaster sagt, ich habe einen Inkrementalgeber.
Jetzt lese ich noch mal weiter vorher von Volvodani
Quote
Der ander Code ist für einen Incrementalgeber bei dem sich die Signale anders verhalten. 

woraus ich schließe dass ich eben keinen Incrementalgeber habe.

Das würde ich nun natürlich gerne mal klären und dann würde ich gerne klären, mit welchem Code ich testen kann/muss.
Mit diesem hier klappts leider nicht:
Code: [Select]
/*  Digital Pin 2 accepts external interrupts. Pin1 of a rotary encoder
    is attached to DigitalPin2. An interrupt routine will be called
    when pin1 changes state, including noise.
    This will be made more efficient with hardware debouncing.
    */
int pin1 = 2;
int pin2 = 3;
int counter;
boolean goingUp = false;
boolean goingDown = false;
void setup()
{
  counter = 0;
  //Serial prints for debugging and testing
  Serial.begin(9600);

/* Setup encoder pins as inputs */
    pinMode(pin1, INPUT); // Pin 2
    pinMode(pin2, INPUT); // Pin 4

// encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, decoder, FALLING);

}

void loop()
{
//using while statement to stay in the loop for continuous
//interrupts
while(goingUp==1) // CW motion in the rotary encoder
{
goingUp=0; // Reset the flag
counter ++;
Serial.println(counter);
}

while(goingDown==1) // CCW motion in rotary encoder
{
goingDown=0; // clear the flag
counter --;
Serial.println(counter);
}
}

void decoder()
//very short interrupt routine
//Remember that the routine is only called when pin1
//changes state, so it's the value of pin2 that we're
//interrested in here
{
if (digitalRead(pin1) == digitalRead(pin2))
{
goingUp = 1; //if encoder channels are the same, direction is CW
}
else
{
goingDown = 1; //if they are not the same, direction is CCW
}
}

uwefed

Versuch mal 2 LEDs mit den Ausgang des Encoders anzusteuern und diesen langsam zu drehen. Wenn die LEDs immer, das eine zeitverzögert zum anderen leuchtet, dann ist es ein incrementeller Encoder mit 90 grad Phasenverschiebung zwischen den beiden Signalen.
Wenn nicht dann mußt Du dir eine Wahrheitstabelle zeichnen.
Grüße Uwe


currymuetze

Hi Uwe,

also mit diesem Code funktionierte es einwandfrei (also der ursprüngliche von mir mal ganz rudimentär aufgebaut):
Code: [Select]

/******Encoder*************/
int AAread=0;
int BBread=0;
byte A=3;
byte B=2;
int d;


/************SETUP*****************/
void setup()
{
 Serial.begin(115200);
/************ENCODER*************/  
 pinMode(A,INPUT);                        
 pinMode(B,INPUT);
 digitalWrite(A,HIGH);                          
 digitalWrite(B,HIGH);                        
 
 attachInterrupt(0,encoder,CHANGE);    
}
/***************HAUPTSCHLEIFE******************/
void loop()
{
    Serial.println("Modus 0 / Secs: ");
    Serial.println(millis());
    delay(1000);
}

/********Encoder Steuerung*****/
void encoder() {
 AAread=digitalRead(A);
 BBread=digitalRead(B);
 /*--Linksdrehen--*/
 if (AAread==LOW && BBread==LOW ||AAread==HIGH&& BBread==HIGH){

      d=d-1;

   Serial.print("Linksdrehen: ");
   Serial.println(d,DEC);    
 }  
 /*--Rechtsdrehen--*/
 if (AAread==HIGH && BBread==LOW||AAread==LOW && BBread==HIGH) {

      d=d+1;

   Serial.print("Rechtsdrehen: ");
   Serial.println(d,DEC);      
 }  
}  


Einziges Manko: Wenn ich nur ein Raster weiterdrehe, löse ich jedesmal 2 bis 4 mal aus, also +1 +1 +1 +1

Dann den Code mal ein wenig erweitert:
Code: [Select]

/******Encoder*************/
int AAread=0;
int BBread=0;
byte A=3;
byte B=2;
int d;


/************SETUP*****************/
void setup()
{
 Serial.begin(115200);
/************ENCODER*************/  
 pinMode(A,INPUT);                        
 pinMode(B,INPUT);
 digitalWrite(A,HIGH);                          
 digitalWrite(B,HIGH);                        
 
 attachInterrupt(0,encoder,CHANGE);    
}
/***************HAUPTSCHLEIFE******************/
void loop()
{
    Serial.println("Modus 0 / Secs: ");
    Serial.println(millis());
    delay(1000);
}

/********Encoder Steuerung*****/
void encoder() {
 AAread=digitalRead(A);
 BBread=digitalRead(B);
 /*--Linksdrehen--*/
 if (AAread==LOW && BBread==LOW ||AAread==HIGH&& BBread==HIGH){
   if (d>20 && d<4000) {  
      d=d-10;
   }
   if ((d<=20 || d>=4000) && d>=1) {  
      d=d-1;
   }

   Serial.print("Linksdrehen: ");
   Serial.println(d,DEC);    
 }  
 /*--Rechtsdrehen--*/
 if (AAread==HIGH && BBread==LOW||AAread==LOW && BBread==HIGH) {
   if (d>20 && d<4000) {  
      d=d+10;
   }
   if ((d<=20 || d>=4000) && d<=4094) {  
      d=d+1;
   }

   Serial.print("Rechtsdrehen: ");
   Serial.println(d,DEC);      
 }  
}    

Irgendwie läuft der Speicher recht schnell voll, wenn ich zu schnell drehe....irgendwann bricht zumindest die seriel Verbindung ab und bleibt stehen bei den d=d+10


Go Up