Schrittzählung mit Inkrementalgeber

Hallo,
ich versuche mit einem Inkrementalgeber die Schritte eines Schrittmotors richtungsabhängig zu zählen. Der Geber liefert auch die richtigen Werte, aber im SerialMonitor kann man erkennen, dass nach wenigen Sekunden keine Werte mehr berechnet/gesendet werden.
Vermutlich habe ich einen Fehler im folgenden Code (vielleicht bei der Verwendung vom Interrupt?), aber ich kann ihn nicht finden:

int A = 2;
int B = 3;
volatile int Schritte = 0;
int Ausgangszustand;
volatile int alterZustand;
volatile int neuerZustand;
volatile int Differenz; 
int SignalA;
int SignalB;

void setup()
{
  attachInterrupt(0,SchritteZaehlen,CHANGE);  //Interrupt Pin2, wenn sich an Ch1 Spannung ändert
  attachInterrupt(1,SchritteZaehlen,CHANGE);
  Serial.begin(9600);
  pinMode(2,INPUT);
  pinMode(3,INPUT);
    
  SignalA=digitalRead(A);
  SignalB=digitalRead(B);
  if(SignalA==1 && SignalB==1){ Ausgangszustand=1; }
  if(SignalA==0 && SignalB==1){ Ausgangszustand=2; }
  if(SignalA==0 && SignalB==0){ Ausgangszustand=3; }
  if(SignalA==1 && SignalB==0){ Ausgangszustand=4; }
  
  alterZustand = Ausgangszustand;
}
           
void loop()
{
  // Wenn Schritte != WGxxxx(angesteuerte Schritte) -> FEHLER
}

void SchritteZaehlen()
{
  //Zustände des Sensors bestimmen (1-4):
  if(SignalA==1 && SignalB==1){ neuerZustand=1; }
  if(SignalA==0 && SignalB==1){ neuerZustand=2; }
  if(SignalA==0 && SignalB==0){ neuerZustand=3; }
  if(SignalA==1 && SignalB==0){ neuerZustand=4; }
  
  Differenz=alterZustand-neuerZustand;
           
  if(Differenz ==1 ||Differenz ==-3)
  {
    Schritte++;
    Serial.print("Schritt ");Serial.println(Schritte);
  }
  if(Differenz ==-1 ||Differenz ==3)
  {
    Schritte--;
    Serial.print("Schritt ");Serial.println(Schritte);
  }
  if(Differenz ==2 ||Differenz ==-2)
  {
    Serial.println("Fehler bei der Bewegungserkennung (Ein Schritt wurde nicht gezählt)!");
  }
  
  alterZustand = neuerZustand;
}

Der SerialMonitor gibt für Differenz und Schrittzahl folgende Werte aus und bricht unvermittelt ab, Daten zu übertragen:

Ausgangszustand: 2
1
Schritt 1
-3
Schritt 2
1
Schritt 3
1
Schritt 4
1
Schritt 5
-3
Schritt 6
1
Schritt 7
1
Schritt 8
1
Schritt 9
-3
Schritt 10
1
Schritt 11
1
Schritt 12
1
Schritt 13
-3
Schritt 14
1
Schritt 15
1
Schritt 16
1
Schritt 17
-3
Schritt 18
1
Schritt 19
1
Schritt 20
1
Schritt 21
-3
Schritt

Zur Erklärung der “Zustände 1-4” habe ich noch ne Grafik angehängt.

Schonmal Vielen Dank für eure Hilfe

Du liest in der Interruptfunktion den Zustand der Eingänge nicht aus. Darum ann das Zählen der Schritte nicht funktionieren. Außerdem ist eine Serial.print() funktion innerhalb der Interruptfunktion() verboten!!! Du blockierst die Interrupfunkion fü ca 10mS bei jedem Durchgang. So verlierst Du leicht schritte.

Braucht Du umbedingt alle 4 Flanken der beiden Signale? Genügt nicht auch eine einfache Genauigkeit? Das würde die Interruprourine stark vereinfachen:

Aufruf der Routine nur bei steigender oder sinkender Flanke von PhaseA void SchritteZaehlen() { Differenz+=(-1+2*digitalRead(pin3)); }

Grüße Uwe

Ich lese doch mit

SignalA=digitalRead(A);
SignalB=digitalRead(B);

die Eingänge des Gebers in der Interrupt-Fkt aus?

Außerdem ist eine Serial.print() funktion innerhalb der Interruptfunktion() verboten!!!

Das habe ich nicht gewusst, danke für die Info. Ich benötige möglichst hohe genauigkeit, deshalb habe ich alle 4 Flanken der Signale verwendet.

Niete: Ich lese doch mit

SignalA=digitalRead(A);
SignalB=digitalRead(B);

die Eingänge des Gebers in der Interrupt-Fkt aus?

Ich sehe diese Funktionen alleinig im setup(). Reden wir beide von 2 verschiedenen Sketches?

Grüße Uwe

Entschuldigung, ich habe diesen Teil wohl beim Kopieren irgendwie vergessen, jedenfalls ist er im Sketch vorhanden:

void SchritteZaehlen()
{
  [b]SignalA=digitalRead(A);[/b]
  [b]SignalB=digitalRead(B);[/b]
     
  //Zustände des Sensors bestimmen (1-4):
  if(SignalA==1 && SignalB==1){ neuerZustand=1; }
  if(SignalA==0 && SignalB==1){ neuerZustand=2; }
  if(SignalA==0 && SignalB==0){ neuerZustand=3; }
  if(SignalA==1 && SignalB==0){ neuerZustand=4; }
  
  Differenz=alterZustand-neuerZustand;

Inzwischen ist Zählen möglich (nachdem ich die serielle Übertragung aus dem Interrupt gelöscht habe), jedoch verzählt der Arduino sich. Woran könnte dies liegen? Ist die Interrupt-Routine zu langsam?

Niete: Inzwischen ist Zählen möglich (nachdem ich die serielle Übertragung aus dem Interrupt gelöscht habe), jedoch verzählt der Arduino sich. Woran könnte dies liegen? Ist die Interrupt-Routine zu langsam?

Möglicherweise oder die Kontakte prellen falls Du einen mechanischen Encoder hast. Grüße Uwe

Mein Inkrementalgeber ist ein optischer Sensor (Vishay TCUT1300XD1).

Vielen Dank nochmal für die Tipps, das Zählen scheint jetzt einigermaßen genau zu funktionieren. Das einzige verbliebene Problem ist nun, dass der Arduino manchmal (wenn der zu beobachtende Motor schon still steht) wahllos weiterzählt.

Fehlen etwa die Pullup Widerstände? Grüße Uwe

Wie genau sind pullUp-Widerstände umzusetzen? Ich dachte, dass der Sensor dauerhaft die Signale für A und B sendet, dann liegt doch immer ein eindeutiger Pegel an, oder?

http://www.vishay.com/docs/84756/tcut1300.pdf

Da sind nur Transistoren die den Pin auf Masse ziehen. Das Plussignal muß von einem Widerstand kommen, der auf +5V geschaltet ist. Du kannst auch den internen Pullupwiderstand aktivieren.

http://arduino.cc/en/Reference/PinMode

Uwe

Wenn ich den internen Pullup aktiviere, also

pinMode(2,INPUT);
 pinMode(3,INPUT);

durch folgendes ersetze

pinMode(2,INPUT_PULLUP);
pinMode(3,INPUT_PULLUP);

zählt der Arduino aber nur einen Bruchteil der Schritte. Oder mache ich da was falsch?

lt. deinem Datenblatt hat der Sensor zwei NPN Emitter, die du schlecht beide auf GND legen kannst um am OpenCollector einen Puls zu messen. Ich würde den C auf 5V legen und an jeden E einen Widerstand gegen GND.

INPUT_PULLUP wäre schon schön gewesen, schade.

Ich habe leider keine Idee, wie das umzusetzen ist, wäre schön wenn da noch genauere Infos kämen. Außerdem ist für mich unerklärlich, wieso der Sensor manchmal einwandfrei funktioniert

Niete:
Ich habe leider keine Idee, wie das umzusetzen ist, wäre schön wenn da noch genauere Infos kämen.

Hab mal ein Bildchen aus deinem Datenblatt etwas ergänzt.
An der Diode sollten bei ca. 330 Ohm nach 5V ca. 10 mA fliessen

DualPhotoTransistor.PNG

Vielen Dank! Ist auch ein PullDown mit 33k möglich?

Ist auch ein PullDown mit 33k möglich?

Ja

Probier’s doch aus. Wenn du den internen Pullup hättest verwenden können, wären das auch ca 30k gewesen.
Die 10k hatte ich in dem Datenblatt gesehen.

Meine erstaunlichsten Anfangs-Erfahrungen bei Digital-Elektronik waren, dass da schonmal eine ganze Zehnerpotenz Toleranz drin ist. Mein erstes Widerstandssortiment hatte nur 1, 2.2, 4.7, 10, … Abstufungen und es war immer was passendes dabei :wink:

Wenn du keine 10k Widerstände hast, aber 33k oder 4.7k würde ich “gefühlsmäßig” eher 4.7k nehmen, aber …
Kaputtgehen kann nichts, solange du über 220 Ohm bleibst :wink:
Hängt evtl. davon ab, ob Fremdlicht in deine Lichtschranke reinfällt. Und wie du überhaupt damit deine “Schritte” erfasst.
Hast du einen Dreh-Encoder zerlegt um zu sehen, was für ein Sensor drin verbaut ist ?

Zum Testen vielleicht mal analog messen, wie (ob) die Signale sich ändern bei 33k und bei was kleinerem.
(muss für brauchbare Digitalsignale entweder < 0.7V oder > 3.3V sein. )
Bei 1 nA Dunkelstrom ( aus dem Datenblatt bei 25°C ) hast du selbst bei 100k nur 0.1 mV.