Zum Inhalt springen

1. Erweiterung: Messung der Drehschritte

Im Video wird gezeigt, was ein Encoder ist und wie er funktioniert.

Linke LichtschrankeRechte LichtschrankeBCode
liefert 0 (LOW)liefert 0 (LOW)0*10+0=0
liefert 1 (HIGH)liefert 0 (LOW)1*10+0=10
liefert 1 (HIGH)liefert 1 (HIGH)1*10+1=11
liefert 0 (LOW)liefert 1 (HIGH)0*10+1=1

Die einfachste Art, die Lichtschrankenzustände zu codieren, wäre einfach die Zustände als Dezimalzahl zu interpretieren.

Allerdings sind Berechnungen im Dezimalsystem wesentlich langsamer als Berechnungen im Binärsystem (2-er System). Und bei sich schnell drehenden Motoren muss sehr oft der Zustand der Lichtschranken berechnet werden

Linke LichtschrankeRechte LichtschrankeBCode
liefert 0 (LOW)liefert 0 (LOW)0*2+0=0
liefert 1 (HIGH)liefert 0 (LOW)1*2+0=2
liefert 1 (HIGH)liefert 1 (HIGH)1*2+1=3
liefert 0 (LOW)liefert 1 (HIGH)0*2+1=1

Im Binärsystem (2-er System) wird die 10 lediglich durch die 2 ersetzt.

Erweiterung der h-Datei

class Motor
{
  private:
  int en,m1,m2,i1,i2;
  const int st[4]={0,2,3,1};   // Abfolge der Zustände
  volatile int pos;      // Aktueller Zustand
  volatile long steps=0; // Einzelschritte (steps)
  
  public:
  
  Motor(int e,int a,int b,int c,int d);
  void power(int p);
  void richtung(); /*
                    * Wird immer ausgeführt, wenn eine Änderung bei 
                    * i1 oder i2 auftritt
                    */
  long getSteps(); // Liefert die bisher gemachten Eintelschritte
                    
};

In der Klasse werden jetzt ein paar Daten mehr gespeichert:

  • i1 und i2 sind die InputPins der beiden Lichtschrankken.
  • st[] ist ein Array, in dem die möglichen Zustände der Lichtschranken gespeichert sind.
  • pos ist die Position des letzten Lichtschrankenzustands in st[].
  • steps ist die Anzahl der Schritte, die der Motor bereits gemacht hat.

Die Konstruktormethode Motor erwartet jetzt zusätzlich noch die beiden Pins der Lichtschranken.

Neu hinzugekommen sind die Methoden richtung() und getSteps().

  • richtung() wird immer dann aufgerufen, wenn sich der Zustrand der Lichtschranken geändert hat. Dann wird die Laufrichtung des Motors ermittelt und steps um 1 erhöht oder erniedrigt.
  • getSteps() liefert die Anzahl der Motorschritte.

Die cpp-Datei

Motor::Motor(int e, int a,int b,int c,int d)
{
  en=e; // Motorpins merken
  m1=a;
  m2=b;
  i1=c;
  i2=d;
  
  pinMode(en,OUTPUT); // Pins konfigurieren
  pinMode(m1,OUTPUT);
  pinMode(m2,OUTPUT);
  pinMode(i1,INPUT);
  pinMode(i2,INPUT);

  digitalWrite(en,LOW); // Motor erst mal ausschalten
  digitalWrite(m1,LOW);
  digitalWrite(m2,LOW);

  int h = digitalRead(i1)+2*digitalRead(i2); // Aktuellen Zustand ermitteln
  int i;
  for(i=0;i<3;i++) if (st[i]==h) pos=i;
}

Im Konstruktor werden werden die Inputpins gespeichert und der Pinmode auf INPUT gesetzt.

Neu sind die letzten drei Zeilen.

Zunächst wird, wie oben beschrieben, der Zustand der Lichtschranken codiert. Anschließend wird die Position des aktuellen Zustands in st[] ermittelt und in pos gespeichert.

void Motor::richtung()
{
  int h = digitalRead(i1)+2*digitalRead(i2);
  if (st[(pos+3)%4]==h)
  {
    steps--;  
    pos=(pos+3)%4;
  }  
  else if (st[(pos+1)%4]==h)
  {
    steps++;  
    pos=(pos+1)%4;
  }
}

Die Methode richtung() ermittelt zunächst den Zustand der Lichtschranken und speichert ihn in h.

In der Tabelle ist der Inhalt des Arrays st[] dargestellt.

posst[pos]
00
12
23
31

pos zeigt auf den letzten Zustand und h stellt den aktuellen Zustand der Lichtschranken dar. Ist beispielsweise  pos==2 und h==2, so hat sich der Motor um einen Schritt rückwärts gedreht, steps muss um 1 verringert werden. Ist h==1, so hat sich der Motor um einen Schritt vorwärts gedreht, steps muss um 1 erhöht werden.
Man muss den aktuellen Zustand h mit st[pos-1] und st[pos+1] vergleichen. Das geht aber schief, wenn pos auf den Rand zeigt, also 0 oder 3 ist. Dann zeigt pos+1=4 oder pos-1=-1 außerhalb des Arrays st[].

-4/4=-1 Rest 00/4=0 Rest 04/4=1 Rest 0
-3/4=0 Rest -31/4=0 Rest 15/4=1 Rest 1
-2/4=0 Rest -22/4=0 Rest 26/4=1 Rest 2
-1/4=0 Rest -13/4=0 Rest 37/4=1 Rest 3

Eine Lösung ist das Rechnen mit Rest. Bei int-Variablen ergibt die Division eine ganze Zahl. Beispiel 7/4=1

Mit % lässt sich der Rest einer Division berechnen. Beispiel 7%4=3

Wenn st[pos] der letzten Zustands der Lichtschranken ist, dann ist st[(pos+1)%4] der nächste und st[(pos+3)%4] der vorherige Zustand.

long Motor::getSteps()
{
  return steps;
}

Die Methode getSteps() liefert die Anzahl der bisherigen Motorschritte.

Anwendung der Klasse Motor und Interrupt

#include"motor.h"
#include <EnableInterrupt.h>

//       en,m1,m2,i1,i2
/*
 * en EnablePin
 * m1 Motorpin1
 * m2 Motorpin2
 * i1 Inputpin1 (Lichtschranke oder Encoder)
 * i2 Inputpin2
 */
Motor M1(6 ,7 ,8 ,2 ,3 );

void setup() 
{
  Serial.begin(9600);
  
  enableInterrupt(2,iRoutine,CHANGE);  // Wenn sich i1 oder i2 ändern, sollen die 
  enableInterrupt(3,iRoutine,CHANGE);  // Motorsteps weitergezählt werden.
}

void loop() 
{
  delay(1000);
  Serial.println(M1.getSteps());
}

void iRoutine() 
{
   M1.richtung();
}

Wie ist es möglich, genau zu dem Zeitpunkte, an dem sich der Zustand einer der Lichtschranken ändert, die Methode richtung() von M1 auszuführen?
Genau das können Interrupts.

Wird durch eine Veränderrung an Pin 2 ein Interrupt ausgelöst, wird das aktuelle Programm unterbrochen und die Interrupt-Routine iRoutine() ausgeführt. Danach wird das Programm an der Stelle fortgesetzt, an der es unterbrochen wurde.

Mit #include <EnableInterrupt.h> wird die Interrupt-Bibliothek eingebunden.

enableInterrupt(2,iRoutine,CHANGE); aktiviert den Interrupt für eine Änderung des Zustandes von Pin 2. iRoutine() wird dann als Interrupt-Routine ausgeführt.

iRoutine() führt lediglich M1.richtung() aus.

Die Bibliothek EnableInterrupt kann von dieser Seite als zip-Datei heruntergeladen werden.

Die zip-Datei muss in das Verzeichnis Arduino/libraries/ entpackt werden.

Das Beispiel kann hier heruntergeladen werden.