Motor Encoder "live" auslesen

Hallo, Zur Ansteuerung eines DC Motors mit Encoder verwende ich einen Motion Controller mit externer Software (Megunolink) über eine serielle Verbindung. Hierbei sendet ein Slider Steuerelement beim Bewegen kontinuierlich serielle Daten an den Motion Controller, welcher den Motor vor- und rückwärts bewegt. Ich würde nun gerne aktuellen Encoderdaten sozusagen "live" auslesen und mit Serial.print ausgeben wenn sich der Motor bewegt. Hier mein Ansatz. Die Steuerung des Motors funktioniert, jedoch werden die Encoderdaten erst ausgegeben, wenn ich dem Controller ein Motor-Stopp Signal gegeben habe. Wenn ich meinen Code ohne die while Schleife in den Loop Abschnitt nehme, funktioniert das ganze bestens, jedoch werden dann auch ständig die Encoderdaten übermittelt, wenn der Motor nicht dreht. Das möchte ich nicht. Warum geht das so nicht wie ich hier versuche? Der Slider sendet die speed Daten. Ich glaube da bräuchte ich nen Denkanstoß :-)

   rc.SpeedAccelM2(address,3200,speed);
readEnc2();

void readEnc2() // read encoder 2
{
    while(enc2 != new_enc2)
  {
new_enc2 = rc.ReadEncM2(address);
delay(100);
enc2 = rc.ReadEncM2(address);
Serial.println(enc2);
  }
 }

Verzichte auf blockierenden Code wie delay() oder while-Schleifen. Das Programm muss die ganze Zeit durchlaufen. Dann liest du regelmäßig deinen Encoder aus. Das kann man z.B. mit einem Timer alle paar ms tun damit es flüssig geht

Dann kann man in loop() einfach zeitgesteuert den aktuellen Wert ausgeben. Siehe das BlinkWithoutDelay Beispiel

Ok - im Loop funktionierts ja ohne die while Schleife. Das delay muss nicht sein, ich wollte nur sicher gehen, dass die Encoder Daten tatsächlich unterschiedlich sind.

Trotzdem: gibts denn keine Möglichkeit das außerhalb der Loop zu machen?

In dem du in loop() eine Funktion aufrufst. Wie sonst?

Am praktischsten ist es halt das Auslesen des Encoders von der Ausgabe zu entkoppeln. Weil ersteres i.d.R. viel öfter als die Ausgabe geschehen muss.

Serenifly: Am praktischsten ist es halt das Auslesen des Encoders von der Ausgabe zu entkoppeln.

Wie muss ich mir das vorstellen?

Hallo,

er meint sicherlich 2 getrennte Funktionen. Eine zum auslesen und eine zum anzeigen. Wichtig ist das auslesen, anzeigen wenn Zeit ist.

Ja so hab ich es auch verstanden. Das ganze geht jetzt grundsätzlich auch mal. Warum ich das Auslesen und das Anzeigen nicht in der Loop haben wollte hatte folgenden Gedanken zugrunde: Ich möchte den Arduino (hier Mega oder Uno) so wenig wie möglich belasten und deshalb nur Auslesen und Anzeigen, wenn es Daten hierzu gibt, also nicht wenn der Motor stillsteht.

Vielleicht hab ich da einen Denkfehler. In der Loop lasse ich auch nur auslesen wenn speed (Motorgeschwindigkeit) größer 0 ist. Die Prüfung läuft ja nun undendlich in der Loop. Ist das belastend für den Arduino? Es soll nun noch ein zweiter Motor hinzu, welcher ebenfalls ausgelesen werden soll und dann werden kontinuierlich Messungen von einem Sensor übertragen. Wann beginnt das ganze zu holpern?

Hallo,

die loop muss ja ohne Ende durchlaufen werden. Geht ja gar nicht anders. Wenn du die Anzeige nur selten aktualsieren möchtest, zum Bsp. aller 1sec., dann mach das zur Bedingung der Anzeigefunktion. Die Funktion wird angesprungen, aha Zeit noch nicht um, sofort wieder verlassen und zur nächsten. Geht auch nicht anders. Das dauert nur paar wenige Takte und man merkt es nicht. Wenn das zum Problem werden sollte, hat man vielleicht andere Probleme oder im seltenden Fall ist der µC doch zu langsam. Mit ordentlicher Programmierung kann auch ein an ATMEGA mit 16MHz ganz schön ackern.

Rechne auch einfach mal die Impulszeiten rückwärts, damit man weiß mit welchen Messintervallen man es zu tun bekommt. Angenommen 10.000 U/min. Dann hat man aller 6ms einen Impuls. Vielleicht grenzwertig mittels Polling. Mit Interrupt jedoch gar kein Problem. Erfordert natürlich überlegten Umgang mit Interrupts usw. Die Frage ist auch wie lang ist der Zählimpuls selbst. Polling wäre auch nicht möglich wenn man 1s Zeit hat jedoch der Impuls davon nur 100µs lang ist.

Ein Bsp. meines seriellen Monitors zum debuggen, aller 500ms eine neue Ausgabe. Vorher wird der die Funktionsaufruf umgehend wieder verlassen. Im Sketch muss nur der “serieller_Monitor” aufgerufen werden.

void serieller_Monitor ()
{
  static unsigned int intervall = 500;
  static unsigned long last_millis = 0;

  if (millis() - last_millis < intervall) return; // Zeit noch nicht erreicht, Funktion abbrechen
  
  last_millis = last_millis + intervall; 
  Ueberschriftszeile();
  Serial.print(Steuerung_NEU); Serial.print('\t');
  Serial.print(Steuerung_IST); Serial.print('\t');
  Serial.println();
}


void Ueberschriftszeile ()
{
  static int counter = 33;

  counter++;
  
  if (counter < 25) return; // aller 25 Ausgaben neue Betreffzeile einfügen
  
  counter = 0; 
  Serial.print(F("S_NEU")); Serial.print('\t');
  Serial.print(F("S_IST")); Serial.print('\t');
  Serial.println();
}

Doc_Arduino: Angenommen 10.000 U/min. Dann hat man aller 6ms einen Impuls. Vielleicht grenzwertig mittels Polling. Mit Interrupt jedoch gar kein Problem. Erfordert natürlich überlegten Umgang mit Interrupts usw. Die Frage ist auch wie lang ist der Zählimpuls selbst. Polling wäre auch nicht möglich wenn man 1s Zeit hat jedoch der Impuls davon nur 100µs lang ist.

Was auch gut geht geht ist Polling in einem Timer Interrupt, z.B. alle 1 oder 2ms.

Hallo,

auch eine gute Idee. Bringt ein gleichmäßiges Abfrageintervall. Die erwartete Impulszeit sollte auch hier gleich + etwas Überlappung der Intervallzeit entsprechen, sonst könnten Impulse verloren gehen. Falls das wichtig ist.

Danke euch - bzgl. Interrupts muss ich mich erst noch einlesen, davon hab ich keine Ahnung.

Ist eigentlich recht einfach. Du definierst einen Pin als Interrupt. Die möglichen Pins sind je nach Arduino typ unterschiedlich. Beim UNO sind es Pin 2 und / oder Pin 3. Es gibt eine Interrupt Funktion die möglichst kurz sein muss. Immer dann wenn eine Unterbrechung an den Pin stattfindet wird diese Funktion angesprungen und ausgeführt. Der normale loop wird hierfür kurzzeitig unterbrochen und nach Ausführung der Interrupt Funktion an der gleichen Stelle wieder fortgesetzt an der er unterbrochen wurde.

Einlesen musst du dich jetzt natürlich immer noch :confused: Ich hoffe aber dass ich dir einen groben Überblick über die Funktionsweise geben konnte. Mir hats "vor'n paar Wochen geholfen :)

Stefan

dr_nobody: Trotzdem: gibts denn keine Möglichkeit das außerhalb der Loop zu machen?

Ausdrücklich nicht im Widerspruch zu #3 möchte ich die Frage anders verstehen und dementsprechend die Antwort etwas anders formulieren.

Außer durch Interrupt nicht. Die Schleife beginnt am Anfang, durchläuft alle Befehle und Funktionen, und endet am Ende der Schleife, und so weiter. Funktionen dienen der Zusammenfassung von Code und der besseren Lesbarkeit durch uns Menschen.

Was Du meines Erachtens benötigst, bezeichen ich mal als Funktionsblöcke. Ein Funktionsblock zum Messen, einer für die Ausgabe. Das wurde in #7 schon schön durch Funktionen realisiert, ginge aber auch ohne. "Vorher wird der Funktionsaufruf umgehend wieder verlassen." ist der springende Punkt, würde ich mit "vorher wird der Funktionsblock umgehend verlassen." formulieren.

Damit erhält man eine quasi parallele Verarbeitung, wenn die Funktionsblöcke blockadearm programmiert werden. Rudi hier aus dem Forum baut sich gerne einen Herzschlag per LED ein. Ohne Herzrhythmusstörungen läuft der Sketch gut durch. Läßt man zwei LEDs mit unterschiedlicher Frequenz blinken, hat man es kapiert.

Möglicherweise hilft Anleitung: Endlicher Automat mit millis()

Sollte ich mit dem Verständnis dieses Themas genial daneben liegen, so keine Sorge, denn ich liege gut ;D

Hallo,

deine Formulierung ist gut. In die Funktion muss ja erstmal reingesprungen werden bevor sie überhaupt verlassen werden kann. :grin: Habe mir oben eine kleine Korrektur erlaubt. Alles neu schreiben wollte ich nun auch nicht. Das mit der Takt-LED Anzeige habe ich auch schon machen müssen zum debuggen. Als ich alleine mit dem seriellen Monitor nicht weiter kam.