I2C Kommunikation bringt Schrittmotor außer Tritt

Hallo zusammen,

ich habe einen kleinen Sketch geschrieben, welches einen bipolaren Schrittmotor welcher, von einem DRV8825 Motortreiber getrieben wird, ansteuern soll. Zusätzlich soll nach jeder ausgeführten Umdrehung ein 16x2 Zeichen LCD Display (HD44780) mit I2C Schnittstelle die Anzahl ausgeführter Umdrehungen ausgeben. Die Ansteuerung des Motors und des I2C Displays funktioniert soweit auch, jedoch benötigt die Übertragung der Display Inhalte über den I2C Bus vermutlich eine Vielzahl an Takten, was zum kurzzeitigen Stocken des Schrittmotors führt.

Da ich bislang keine Idee habe wie ich das Timing Problem in den griff bekommen soll hoffe ich, dass ihr mir weiter helfen könnt.

Als Testumgebung wird ein Arduino Mega 2560 r3 und ein Ramps 1.4 shield verwendet.

// Dieser Sketch soll einen bipolaren Schrittmotor, welcher von einem DRV8825 Motor Treiber versorgt wird ansteuern.
// Nach jeder erfolgten Umdrehung soll auf dem Angeschlossenen 16x2 Zeichen LCD Display (HD44780) mit I2C Schnittstelle die Ausgeführten Umdrehungen Angezeigt werden.
//
// Bemerkung: Der Sketch ist legiglich für Tests. Nach den im Sketch definierten 100 Umdrehungen des Schrittmotors muss der Arduino resettet werden um das Programm erneut zu starten.
// 
// Problem: Die I2C Kommunikation benötigt eine vielzahl an takten des Prozessors, was zum Kurzzeitigen Stocken des Schrittmotors führt.
//

#include <AccelStepper.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h> 

// Definition der Schrittmotoranschlüsse und Motorschnittstellentyp. Der Typ der Motorschnittstelle muss bei Verwendung eines Treibers auf 1 gesetzt werden:
#define dirPin 48
#define stepPin 46
#define enablePin 62
#define motorInterfaceType 1

int NumOfStepsForRot = 200;  // Anzahl Schritte die für eine Umdrehung benötigt werden

// Erzeugt eine neue instance von der AccelStepper class:
AccelStepper stepper1 = AccelStepper(motorInterfaceType, stepPin, dirPin);

LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  // Initialisiert die Bibliothek und meldet den Arduino als Busmaster an.
  Wire.begin();
  // Setzt die Taktfrequenz für den I2C-Bus (Versuch um die Übertragung zu beschleunigen)
  Wire.setClock(400000L);
  
  // Initialisiert den I2C LCD Display
  lcd.init();
  // Einschalten die Hintergrundbeleuchtung
  lcd.backlight();
  // Setzen des Cursors an Position: Spalte 1 Zeile 1
  lcd.setCursor(0, 0);
  // Übergeben des auszugebenden Inhaltes
  lcd.print("Anzahl Umdrehungen: ");
  
  // Setzen der Maximalen Geschwindikeit in Schrippen pro Sekunde
  stepper1.setMaxSpeed(900.0);
  // Setzen der Beschleunigung in Schritten pro Sekunde
  stepper1.setAcceleration(50.0);
  // Setzen des Umdrehungsziels z.B. 200 Schritte
  stepper1.moveTo(NumOfStepsForRot*100);
  // Definieren des Einschalt Pins für den Motor Treiber
  stepper1.setEnablePin(enablePin);
  // Pegellogik definieren von Schritt, Richtung und des Einschalt Pin
  stepper1.setPinsInverted(false, false, true);
}

void loop() {
    
    // Wenn Zielposition erreicht wurde Motor deaktivieren
    if (stepper1.distanceToGo() == 0)
      stepper1.disableOutputs();
      
    // Starten des Motor Jobs
    stepper1.run();
	
	// LCD Ausgabe der ausgeführten Umdrehungen
    if ((stepper1.currentPosition() % NumOfStepsForRot) == 0) {
      updateDisplay();
    }
}

// Funktion zur Aktualisierung des I2C LCD Displays
void updateDisplay() {
	  // Setzen des Cursors an Position: Spalte 1 Zeile 2
      lcd.setCursor(0, 1);
	  // Übergeben der Anzhal an Umdrehungen des Schrittmotors
      lcd.print(int(stepper1.currentPosition()/NumOfStepsForRot));
}

AccelStepper_I2C_LCD_16x2.txt (2.84 KB)

Hallo,

die Methode stepper.run() erzeugt mit jedem Aufruf die Impulse für den Motor. Wenn jetzt eine andere Funktion bearbeitet wird können für die Laufzeit der Funktion keine weiteren Impulse erzeugt werde. Folge der Motor bleibt stehen.

Eventuell klappt das was Du vorhast wenn Du den Motor zunächst mal ganz langsam laufen lässt und dann schrittweise schneller wirst.

Heinz

Hi

Bin aktuell schwer von den Combi-Tasks begeistert.
In meinem aktuellem Projekt rufe ich alle Funktionen in der loop() der Reihe nach auf.
Jede der Funktionen prüft nach jedem Durchlauf, ob 'die Zeit um ist', oder ob die Funktion fertig geworden ist.
Bei Beidem beendet sich die Funktion, das Programm geht zurück zur loop() und ruft die nächste Funktion auf.
Bei einer Maximal-Zeit von 10ms kann also jede Funktion innerhalb der 10ms beliebig oft starten (und jeweils einen Teil der Aufgabe ausführen).
Wenn nach der Ausführung die 10ms um sind (oder die Funktion das Ende erreicht hat), gibt Sie die Kontrolle wieder zurück.
Keine Ahnung, ob Das für einen Stepper schnell genug wird - vermutlich nicht - ein Versuch wäre Das aber wert.

Einer der Tabs, also eine der Funktionen.
Sie liest die nächste Nachricht ein, wenn READ- und WRITE-Zeiger verschiedene Werte haben.
Aus dem Task-Makro springe ich in eine eigene Funktion, da ich INNERHALB der Task-Makros keine Switch-Konstrukte verwenden kann (die Task-Makros selber sind ein Switch-Konstrukt).
Auf das Pointer++ musste ich auslagern, da ein
pointer=++pointer%maxpointer;
eine Warnung wirft. (erhöht den aktuellen Zähler um 1 und bricht den Wert am Maxwert um)

Task checkcan() {
  millisnow = millis();       //Startzeit des Task merken
  boolean readynow2 = false;   //ist die Aufgabe des Task erledigt? Dann auf TRUE setzen
  taskBegin();
  while (1) // diese Schleife blockiert nicht
  {
    //read_in_puffer != write_in_puffer --> es gibt Arbeit
    //Nachricht aus read_in_puffer auslesen & interpretieren
    if (read_in_pointer != write_in_pointer) {
      pruefenachricht(nachricht_in[read_in_pointer]);   //übergibt die Nachricht als Referenz, separate Funktion, damit ich, außerhalb der Task-Macros, mit SWITCH arbeiten kann
      //und Pointer einen hoch setzen
      read_in_pointer = plus_eins(read_in_pointer);
    } else {
      //nix zu tun, dann brauchen wir auch nicht auf graue Haare warten
      readynow2 = true;
    }

    //Funktion beenden, wenn wir fertig sind, oder die Zeit für den Task abgelaufen ist
    if (readynow2 || millis() - millisnow >= tasktime) {
      taskSwitch(); //Hier unterbrechen - beim nächsten Aufruf starten wir dann hier
    }
    //...mache irgendwas danach - oder am While-Schleife wieder zum Start Derer
  }
  taskEnd();
}

Wenn keine weitere Nachricht mehr anliegt, oder die Zeit um ist, wird die Funktion verlassen.

Eingebunden im Sketch 'gaaanz vorne' mit
#include <TaskMacro.h> //Combie - Aufsplitten der ganzen Arbeiten, Abbrechen, wenn gewisse Zeit vergangen ist

Mein ganzer Verhau wuchs aus dem Ampel-Beispiel empor ...

Du kannst die I²C-Kommunikation auch auf 400kHz anheben (Tommy56 hatte dazu irgend was geschrieben, müsste suchen).
Sonst wären vll. noch die MoBaTools vom MicroBahner hier aus dem Forum (direkt aus der IDE installierbar) einen Blick wert - dort wird der Stepper im Hintergrund von einem Timer bedient - kein regelmäßiges 'stepper.run();' nötig - selbst delay() (böööse !!1ELF11!!) stört den Stepper dort nicht!

Wege gibt's da viele - welche Brauchbar sind, wird wohl erst ein Versuch zeigen.

MfG

Danke für eure schnelle Hilfe!

@Rentner / Heinz
Ich habe die Drehzahl auf ca. 20 Umdrehungen pro Minuten herabgesetzt, was im Vollschrittbetrieb (1,8° pro Schritt) ca. 67 Schritte pro Sekunde sind. Der Schrittmotor läuft bei der Geschwindigkeit auch ohne Probleme und gerät nicht außer tritt. Dennoch ist das Aktualisieren des I2C Displays im Schrittverhalten des Motors gut zu erkennen.

@postmaster-ino
Der Versuch die I²C-Kommunikation auch auf 400kHz zu setzen hat leider auch nicht so viel gebracht wie erhofft. Das Problem besteht weiterhin.

Aber das mit den Combi-Tasks hört sich nach einer guten Sache an! Werde ich heute Abend mal testen.

Meine aktuelle Überlegung ist, einen zweiten Arduino zu nutzen, welcher dann lediglich für die Ansteuerung des Motors zuständig ist und über die serielle Schnittstelle die Befehle des 1. Arduinos, welcher dann als HMI (human machine interface) Kontroller dienen soll, entgegen nimmt.

ABER das war eigentlich nicht der Plan des Projektes :wink:

I2C (wire.h) und Accelstepper geht nicht gut zusammen. Beim Sendeaufruf blockiert die wire-Lib kurz ( ms-Bereich ). Entsprechend verzögert sich auch dein stepper.run Aufruf, was zum Stocken des Steppers führt. Da dürften auch die combie-Tasks nichts dran ändern können.

2 Arduinos dafür zu nehmen ist aber arg 'oversized' :wink:

Ob MicroBahner wohl zu bescheiden ist?

Seine MobaTools sollten für Steppermotoren auch geeignet sein und timergesteuert arbeiten.
Ist das nicht die Lösung des Problems?

Jap MicroBahner war zu bescheiden :D!

Habe gerade den Skatch auf seine MobaTools.h library umgebaut und siehe da alles läuft ohne Probleme!

Zusätzlich habe ich jetzt eine IR-Reflexions-Lichtschranke (TCRT5000) angeschlossen und schnell zwei teile gedruckt um die Drehzahl effektiv zu Messen. Nun kann man, wenn man denn wollte nach jeder Umdrehung die gemessene Umdrehungsfrequenz auf dem I2C-Display ausgeben lassen.

Danke euch!

LimitlessMind:
Jap MicroBahner war zu bescheiden :D!

Der postmaster hatte den Vorschlag ja schon gemacht :wink:

Freut mich, wenn Dir meine MobaTools helfen 8)