Drehgeber/Encoder auslesen mit Mega2560

Hallo,
ich bin absoluter Neuling und versuche mich an dem Arduino Mega2560.
Ich möchte gerne von einem Motor die Drehzahl auslesen.
Dazu habe ich von der Firma TR-Electronic einen Drehgeber (IMV36-00003).
Programmieren würde ich dies sehr gerne mit Matlab bzw. Simulink, da ich einen PID-Regler aufbauen will (dies ist aber momentan noch nicht in Sicht).
Wie bekomme ich das am besten hin? Hat mir jemand einen Tipp?

Danke schon mal im Voraus! :smiley:

Grüße!

Also als erstes fehlt mir der Link zu Deiner Hardware (dem Drehgeber).

Wie möchtest Du den Arduino mit Matlab/Simulink programmieren? Das sind Programme, die auf einem PC laufen, also gehe ich davon aus, dass Du Deine Applikation irgendwie zweiteilen willst, ein Teil erledigt der Arduino, den anderen übernimmt der PC, aber Du hast uns noch nicht verraten, wie diese Aufteilung aussehen soll. Einen PID-Regler kannst Du auch auf dem Arduino realisieren, dafür bräuchtest Du den PC nicht. Du solltest uns also genauer erklären, wie Dein Projekt aussehen soll, bzw. was Du erreichen willst.

Hallo,

vielen Danke für die Antwort!
Hier der Link zur Hardware: http://www.tr-electronic.de/f/IMV36-INK-1-D-1
Mit einer Impulszahl von 2048

Also zu meinem Projekt:
Ich möchte mit dem Arduino und dem Drehgeber die Momentan-Drehzahl eines Motors auslesen.
Diese Drehzahlwerte sollen dann auf dem Serial-Port ausgelesen werden.

Dies möchte ich gerne mit Simulink auf den Arduino programmieren.
Hoffe mal ich habe dies jetzt verständlich erklärt :wink:

Für Drehimpulsgeber gibt es hier Code:
http://www.mikrocontroller.net/articles/Drehgeber

Das ist C und AVR allgemein und muss daher angepasst werden (vor allem die Interrupts), aber die Algorithmen funktionieren im Prinzip genauso auf dem Arduino.

Oder funktioniert der anders als einfach nur zwei phasenverschobene Impulse auszugeben? Das ist aus dem Datenblatt nicht ganz ersichtlich.

Hallo,
Vielen Dank für die Antwort!

Ich habe mittlerweile zwar auch eine Bib für den Arduino gefunden “rotaryencoder”.

Da ich das ganze jedoch mit Simulink also grafischer Programmierung umsetzen möchte scheidet dieser Weg meines Wissens aus?!

Du kannst den Arduino nicht direkt mit Simulink programmieren. Das normale ist die Standard Arduino IDE. Außerdem gibt es noch Eclipse (was aber kompliziert einzurichten ist), oder das VisualMicro Plugin für VisualStudio (nur die Vollversion), bzw. Atmel Studio (gibts kostenlos).

Programmiert wird aber in C/C++.Wenn du da etwas graphisch Visualisieren möchtest musst die die Daten mit Serial raus schicken (über USB) und auf dem PC entsprechend einlesen und verarbeiten.

Nicht? Komisch das tue ich momentan aber...es gibt von Mathworks eine Toolbox:

Ok, kannte ich nicht. Dann lad dir das halt mal runter und probiere es aus :slight_smile:

Mir scheint das aber nicht wirklich eine vernünftige Alternative zur direkten Programmierung zu sein. Von der Beschreibung ist das eher zum Lernen gedacht und nicht für richtige Anwendungen.

Hab ich breits schon und habe schon ein paar sachen gemacht. Vorteile ist, dass man PID-Regler einfach auslegen kann da einem
die gesamte Simulink-Bib zur verfügung steht. Da das Ziel des Projekts ist einen solchen Regler aufzubauen würde ich es sehr gerne in Simulink machen.
Der Regler ist auch nicht das momentane Problem, ich benötige erst einmal alle Datenquellen.
Und eben die erste wäre die Drehzahl.

Das heißt, dass Ihr noch nicht mit der Simulink-Toolbox gearbeitet habt für Arduino

Das ist eben das Problem mit sowas. Libraries für verschiedene Hardware findest du genug. Die sind aber halt alle in C geschrieben. Das hatte ich auch schon bei anderen Sachen, wo eine Plattform ein paar gute Eigenschafte hatte, aber durch den schlechten Support im Vergleich zu anderen Optionen unattraktiv wurde.

Ein PID-Regler in C ist aber auch nicht besonders schwer. Und auch das gibt es fertig:
http://playground.arduino.cc/Code/PIDLibrary

Gibt auch noch andere andere Lib mit der man die Parameter bestimmen kann.

Hallo,
du hattest wohl recht! Werde das Problem so in die Hand nehmen!

Habe schon mal angefangen die Geschwindikeit aufzunehmen.
Jedoch stimmt dabei etwas nicht…ich mache nichts am Drehgeber aber mal kommt 0 dann mal 300 als ergebnis heraus
Das Datenblatt des Drehgeber den ich verwende habe ich im Anhang

Hier mal die Programmierung:

void setup() {
  pinMode(2, INPUT);                        // Eingangspin auf Eingang
  Serial.begin(9600);
  attachInterrupt(0, readmicros, RISING );  // Interrupt 0 auf Routine
}

volatile unsigned long dauer=1;             // microsekunden
volatile unsigned long last=0;              // Zählerwert
long drehzahl;                              // selbstredend
char buf[17];                               // Pufferstring für sprintf
long faktor=0.029296875;                    // faktor = 60 Sekunden / Inkrementale des Drehgebers -> Umdrehungen pro Minute

void readmicros() {                         // Interrupt-Routine
  unsigned long m = micros();               // Microsekunden auslesen
  dauer = m - last;                         // Differenz letzte
  last = m;                                 // Letzten Wert merken
}

void loop() {                               // Hauptprogramm
  drehzahl = faktor*dauer;             // Drehzahl ausrechnen  
  Serial.println(drehzahl);                 // Puffer ausgeben
  delay(500);  
}

ext_datasheet_DE_IMV36-00003.pdf (259 KB)

IMV36-INK-1-D-1-00.pdf (740 KB)

micros() und millis() funktionieren in Interrupt-Routinen nicht korrekt (d.h. sie werden nicht inkrementiert), da der Timer mit dem diese gezählt werden selbst mit einem Interrupt läuft.

Danke für die Antwort!
Gibts dazu eine alternative?

Serenifly:
micros() und millis() funktionieren in Interrupt-Routinen nicht korrekt (d.h. sie werden nicht inkrementiert), da der Timer mit dem diese gezählt werden selbst mit einem Interrupt läuft.

Es ist richtig, dass sie innerhalb der interrupt-Routine nicht incrementiert werden.
Aber das tut ja nichts zur Sache, da readmicros() ja schnell vorbei ist.

Ein Problem ist, dass sich keiner für die gemessene dauer zwischen zwei Interrupten interessiert, denn loop() hängt meist im delay.
Das zweite Problem ist, dass dauer größer als ein byte ist, und daher nicht immer am Stück in loop übernommen wird.
Das dritte Problem ist, dass ein long eine ganze Zahl (integer) ist, und bei

  long faktor=0.029296875;

der Compiler fies grinst, weil es syntaktisch richtig ist, aber kein Unterschied zu

long faktor = 0;

Was passiert denn bei

void loop() {
  noInterrupts();
    unsigned long d = dauer;
  interrupts();
  Serial.println(d*2929/100000);    
  delay(500);
}

Damit kriegst du wenigstens alle halbe Sekunde die gerade letzte dauer mit Umrechnung angezeigt.
Ob du lieber einen Mittelwert der letzten halben Sekunde haben willst, musst du wissen.

michael_x:

Serenifly:
micros() und millis() funktionieren in Interrupt-Routinen nicht korrekt (d.h. sie werden nicht inkrementiert), da der Timer mit dem diese gezählt werden selbst mit einem Interrupt läuft.

Es ist richtig, dass sie innerhalb der interrupt-Routine nicht incrementiert werden.
Aber das tut ja nichts zur Sache, da readmicros() ja schnell vorbei ist.

Innerhalb der Interrupt-Routine werden micros() und millis() nicht incrementiert, zwischen den Interrupt-Routine-Aufrufen aber schon. Die Interrupt-Routine soll so kurz wie möglich sein und ist deshalb schnell abgearbeitet. Die Zeit wird ein klein wenig zu langsam sein aber das ist meist unbedeutend für die korrekte Funktion des Sketches.

Grüße Uwe

Hallo,
VIELEN DANK FÜR EURE ANTWORTEN!
Hab mir nochmals das ganze Problem angeguckt und etwas rum gegooglt.
Ich denke zudem, dass die Standart Read befehle zu langsam sind.
Deswegen habe ich mit diesen Code abgeändert:
http://www.hessmer.org/blog/2011/01/30/quadrature-encoder-too-fast-for-arduino/

Jedoch bekomm ich beim Kompilieren folgenden Fehler:
sketch_jul24a.ino: In function ‘void HandleInterruptA()’:
sketch_jul24a:37: error: invalid type argument of ‘unary *’

Hier mein aktueller Code:

#include <digitalWriteFast.h>  // library for high performance reads and writes by jrraines
                               // see http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1267553811/0
                               // and http://code.google.com/p/digitalwritefast/

// Quadrature encoders
#define c_EncoderInterrupt 4
#define c_EncoderPinA 19
#define c_EncoderPinB 25
#define EncoderIsReversed

volatile bool _EncoderBSet;
volatile long _EncoderTicks = 0;

void setup()
{
  Serial.begin(115200);
  // Quadrature encoders
  // encoder
  pinMode(c_EncoderPinA, INPUT);      // sets pin A as input
  digitalWrite(c_EncoderPinA, LOW);  // turn on pullup resistors
  pinMode(c_EncoderPinB, INPUT);      // sets pin B as input
  digitalWrite(c_EncoderPinB, LOW);  // turn on pullup resistors
  attachInterrupt(c_EncoderInterrupt, HandleInterruptA, RISING);
}

void loop()
{
  Serial.print(_EncoderTicks);
  Serial.print("\t");
  delay(20);
}

// Interrupt service routines for the left motor's quadrature encoder
void HandleInterruptA()
{
  // Test transition; since the interrupt will only fire on 'rising' we don't need to read pin A
  _EncoderBSet = digitalReadFast(c_EncoderPinB);   // read the input pin
  // and adjust counter + if A leads B
  #ifdef EncoderIsReversed
    _EncoderTicks -= _EncoderBSet ? -1 : +1;
  #else
    _EncoderTicks += _EncoderBSet ? -1 : +1;
  #endif
}

Ohne das digitalReadFast kompiliert es für mich. Damit ist der Fehler entweder in der Lib oder eher darin wie du sie benutzt.

Hast du mal versucht die Pins als int zu definieren? Vielleicht stopelert er da über die defines.

Im Beispiel-Code steht das so:
if((int) digitalReadFast(5) != LOW)

Hallo Serenifly,

habe ich breits ausprobiert. An dem Fehler ändert dies leider nichts.
Aber Danke für den Hinweis!

Habe diesen Beitrag gefunden nach langem googeln:
http://forum.arduino.cc/index.php?topic=176263.0

Damit klappts!
Der Arduino gibt nun die richtigen "Ticks" aus!
Nun werde ich mich an die Geschwinigkeit machen!

Ja, das hatte ich auch gemerkt als ich es richtig eingebunden habe :slight_smile:

Alternativ kannst du sowas auch schneller per Hand schreiben:

Sowas sollte eigentlich schnell genug sein:

my_var = (PIND & (1<<PD1));

Oh gut zu wissen! DANKE!!

Gibts noch einen Tipp wie ich den Code am besten erweiteren kann in Richtung Geschwinkigkeit?
Zeitfunktionen kann ich ja nicht anwenden bezüglich der Interrupts so wie ich das jetzt mitbekommen habe.