Schrittmotoren gleichzeitig ansteuern

Hallo zusammen,

ich möchte 3 Schrittmotoren über H-Brücken mit einem Arduino mega gleichzeitig ansteuern. Sie sollen unabhängig, teilweise gleichzeitig voneinander laufen. Unabhängig funktioniert das schon. Also alle Abläufe nacheinander. Angefügt mein Code.

In der loop-Funktion sollen die Parameter Speed und Steps der einzelnen Motoren geändert werden.

Danke schonmal.

Robotersteuerung.ino (3.94 KB)

Bitte stelle den Sketch direkt rein und setze ihn in Code-Tags (oben links bei Edit </>).

Da kann man ihn besser lesen.

Gruß Tommy

//Motoren Drehrichtungen benennen
byte Motor1Stop = 0;
byte Motor1Rechts = 1;
byte Motor1Links = 2;

byte Motor2Stop = 0;
byte Motor2Rechts = 1;
byte Motor2Links = 2;

byte Motor3Stop = 0;
byte Motor3Rechts = 1;
byte Motor3Links = 2;

//Phasenansteuerung festlegen
byte StepValues [][4] = {
                        {0,0,0,1},
                        {0,0,1,1},
                        {0,0,1,0},
                        {0,1,1,0},
                        {0,1,0,0},
                        {1,1,0,0},
                        {1,0,0,0},
                        {1,0,0,1},
                        };
                        
//MotorAusgaenge festlegen
byte Motor1_Ausgaenge[] = {53,51,49,47}; //IN1 = 47, IN2 = 49, IN3 = 51, IN4 = 53
byte Motor2_Ausgaenge[] = {45,43,41,39}; //IN1 = 39, IN2 = 37, ...
byte Motor3_Ausgaenge[] = {37,35,33,31}; //IN1 = 31, IN2 = 33, ...

//Ausgaenge als Ausgaenge deklarieren und auf "0" setzen
void setup() {
  for (byte i=0; i<=3; i++){
    pinMode((Motor1_Ausgaenge[i]), OUTPUT);
    pinMode((Motor2_Ausgaenge[i]), OUTPUT);
    pinMode((Motor3_Ausgaenge[i]), OUTPUT);
    digitalWrite(Motor1_Ausgaenge[i], LOW);
    digitalWrite(Motor2_Ausgaenge[i], LOW);
    digitalWrite(Motor3_Ausgaenge[i], LOW);
  }
}

void loop() {
  delay(1000);
  Motor1Move(Motor1Rechts, 2, 100);
  Motor2Move(Motor2Rechts, 3, 1000);
  Motor3Move(Motor3Links, 3, 500);
  delay(1000);
  Motor1Move(Motor1Links, 2, 512);
  Motor2Move(Motor2Links, 3, 1000);
  Motor3Move(Motor3Rechts, 5, 300);
  delay(1000);
}

///////////////////////////MOTOR 1///////////////////////////
void Motor1Move(byte Direction1, int Speed1, int Steps1){
  
  //Motor1 Stop
  if(Direction1 == 0){
    for (byte i=0; i<=3; i++){
      digitalWrite(Motor1_Ausgaenge[i], LOW);
     }
  }
  //Motor1 Rechtsdrehung
  if(Direction1 == 1){
    for(int i=0; i<= Steps1; i++){
      for(int j=0; j<=7; j++){
        for(int k=0; k<=3; k++){
          digitalWrite(Motor1_Ausgaenge[k], StepValues[j][k]);
        }
        delay(Speed1);
      }
    }
   Motor1Move(Motor1Stop, 0,0);
   }
  //Motor1 Linksdrehung
  if(Direction1 == 2){
    for(int i=0; i<= Steps1; i++){
      for(int j=0; j<=7; j++){
        for(int k=0; k<=3; k++){
          digitalWrite(Motor1_Ausgaenge[k], StepValues[j][3-k]);
        }
        delay(Speed1);
      }
    }
  Motor1Move(Motor1Stop, 0, 0);
  }
}
///////////////////////////MOTOR 2///////////////////////////
void Motor2Move(byte Direction2, int Speed2, int Steps2){
  
  //Motor2 Stop
  if(Direction2 == 0){
    for (byte i=0; i<=3; i++){
      digitalWrite(Motor2_Ausgaenge[i], LOW);
     }
  }
  //Motor2 Rechtsdrehung
  if(Direction2 == 1){
    for(int i=0; i<= Steps2; i++){
      for(int j=0; j<=7; j++){
        for(int k=0; k<=3; k++){
          digitalWrite(Motor2_Ausgaenge[k], StepValues[j][k]);
        }
        delay(Speed2);
      }
    }
   Motor2Move(Motor2Stop, 0,0);
   }
  //Motor2 Linksdrehung
  if(Direction2 == 2){
    for(int i=0; i<= Steps2; i++){
      for(int j=0; j<=7; j++){
        for(int k=0; k<=3; k++){
          digitalWrite(Motor2_Ausgaenge[k], StepValues[j][3-k]);
        }
        delay(Speed2);
      }
    }
  Motor2Move(Motor2Stop, 0, 0);
  }
}
///////////////////////////MOTOR 3///////////////////////////
void Motor3Move(byte Direction3, int Speed3, int Steps3){
  
  //Motor3 Stop
  if(Direction3 == 0){
    for (byte i=0; i<=3; i++){
      digitalWrite(Motor3_Ausgaenge[i], LOW);
     }
  }
  //Motor3 Rechtsdrehung
  if(Direction3 == 1){
    for(int i=0; i<= Steps3; i++){
      for(int j=0; j<=7; j++){
        for(int k=0; k<=3; k++){
          digitalWrite(Motor3_Ausgaenge[k], StepValues[j][k]);
        }
        delay(Speed3);
      }
    }
   Motor3Move(Motor3Stop, 0,0);
   }
  //Motor3 Linksdrehung
  if(Direction3 == 2){
    for(int i=0; i<= Steps3; i++){
      for(int j=0; j<=7; j++){
        for(int k=0; k<=3; k++){
          digitalWrite(Motor3_Ausgaenge[k], StepValues[j][3-k]);
        }
        delay(Speed3);
      }
    }
  Motor3Move(Motor3Stop, 0, 0);
  }
}

Hallo, willkommen im Forum!

Du benötigst einen endlichen Automaten (Ablaufsteuerung) und millis() anstelle delay(). Für beide Fragestellungen findest Du hier im Forum etliche Beiträge. Beispielsweise kannst Du nach "agmue anleitung" suchen.

delay() wartet einfach die angegebene Zeit ab. Darum muß es raus.

Mit millis() wird geschaut ob es schon wieder Zeit ist etwas zu machen und somit kann quasi gleichzeitig mehrere Aktionen gemacht werden.

Grüße Uwe

Ich danke euch! ich werde es morgen mal in der Praxis ausprobieren und schauen ob es so funktioniert wie ich es gerne hätte.

Gruß Jakob

Du kannst es dir auch einfacher machen und die AccelStepper library benutzen. Die kann mehrere Motoren gleichzeitig ansteuern und du kannst da auch mit Beschleunigungsrampen arbeiten.
Beispiele findest du hier mit der Forum-Suche.

Beschäftige mich leider erst seit wenigen Tagen mit Arduino und habe deshalb dementsprechend wenig Ahnung :confused:

Leider funktioniert es weiterhin nicht dass sich die Motoren gleichzeitig drehen. Ich vermute es liegt an dem delay(Speed) in den Drehbefehlen. Kann ich diese einfach durch millis() ersetzen? löst das mein Problem?

Danke im Voraus.

//Motoren Drehrichtungen benennen
byte Motor1Stop = 0;
byte Motor1Rechts = 1;
byte Motor1Links = 2;

byte Motor2Stop = 0;
byte Motor2Rechts = 1;
byte Motor2Links = 2;

byte Motor3Stop = 0;
byte Motor3Rechts = 1;
byte Motor3Links = 2;
const int motorPause = 1000;

unsigned long motorIntervall;
unsigned long motorMillis;
byte zustand = 0;

//Phasenansteuerung festlegen
byte StepValues [][4] = {
                        {0,0,0,1},
                        {0,0,1,1},
                        {0,0,1,0},
                        {0,1,1,0},
                        {0,1,0,0},
                        {1,1,0,0},
                        {1,0,0,0},
                        {1,0,0,1},
                        };
                        
//MotorAusgaenge festlegen
byte Motor1_Ausgaenge[] = {53,51,49,47}; //IN1 = 47, IN2 = 49, IN3 = 51, IN4 = 53
byte Motor2_Ausgaenge[] = {45,43,41,39}; //IN1 = 39, IN2 = 41, ...
byte Motor3_Ausgaenge[] = {37,35,33,31}; //IN1 = 31, IN2 = 33, ...

//Ausgaenge als Ausgaenge deklarieren und auf "0" setzen
void setup() {
  for (byte i=0; i<=3; i++){
    pinMode((Motor1_Ausgaenge[i]), OUTPUT);
    pinMode((Motor2_Ausgaenge[i]), OUTPUT);
    pinMode((Motor3_Ausgaenge[i]), OUTPUT);
    digitalWrite(Motor1_Ausgaenge[i], LOW);
    digitalWrite(Motor2_Ausgaenge[i], LOW);
    digitalWrite(Motor3_Ausgaenge[i], LOW);
  }
}

void loop() {
   if(millis() - motorMillis >= motorIntervall){
    switch(zustand){
      case 0:
        Motor1Move(Motor1Rechts, 2, 500);
        Motor2Move(Motor2Rechts, 2, 500);
        zustand = 1;
        motorMillis = millis();
        motorIntervall = motorPause;
        break;
      case 1:
        Motor1Move(Motor1Links, 2, 500);
        Motor2Move(Motor2Links, 2, 500);
        zustand = 0;
        motorMillis = millis();
        motorIntervall = motorPause;
        break;
    }  
    }
   }
  
///////////////////////////MOTOR 1///////////////////////////
void Motor1Move(byte Direction1, int Speed1, int Steps1){
  
  //Motor1 Stop
  if(Direction1 == 0){
    for (byte i=0; i<=3; i++){
      digitalWrite(Motor1_Ausgaenge[i], LOW);
     }
  }
  //Motor1 Rechtsdrehung
  if(Direction1 == 1){
    for(int i=0; i<= Steps1; i++){
      for(int j=0; j<=7; j++){
        for(int k=0; k<=3; k++){
          digitalWrite(Motor1_Ausgaenge[k], StepValues[j][k]);
        }
        delay(Speed1);
      }
    }
   Motor1Move(Motor1Stop, 0,0);
   }
  //Motor1 Linksdrehung
  if(Direction1 == 2){
    for(int i=0; i<= Steps1; i++){
      for(int j=0; j<=7; j++){
        for(int k=0; k<=3; k++){
          digitalWrite(Motor1_Ausgaenge[k], StepValues[j][3-k]);
        }
        delay(Speed1);
      }
    }
  Motor1Move(Motor1Stop, 0, 0);
  }
}
///////////////////////////MOTOR 2///////////////////////////
void Motor2Move(byte Direction2, int Speed2, int Steps2){
  
  //Motor2 Stop
  if(Direction2 == 0){
    for (byte i=0; i<=3; i++){
      digitalWrite(Motor2_Ausgaenge[i], LOW);
     }
  }
  //Motor2 Rechtsdrehung
  if(Direction2 == 1){
    for(int i=0; i<= Steps2; i++){
      for(int j=0; j<=7; j++){
        for(int k=0; k<=3; k++){
          digitalWrite(Motor2_Ausgaenge[k], StepValues[j][k]);
        }
        delay(Speed2);
      }
    }
   Motor2Move(Motor2Stop, 0,0);
   }
  //Motor2 Linksdrehung
  if(Direction2 == 2){
    for(int i=0; i<= Steps2; i++){
      for(int j=0; j<=7; j++){
        for(int k=0; k<=3; k++){
          digitalWrite(Motor2_Ausgaenge[k], StepValues[j][3-k]);
        }
        delay(Speed2);
      }
    }
  Motor2Move(Motor2Stop, 0, 0);
  }
}
///////////////////////////MOTOR 3///////////////////////////
void Motor3Move(byte Direction3, int Speed3, int Steps3){
  
  //Motor3 Stop
  if(Direction3 == 0){
    for (byte i=0; i<=3; i++){
      digitalWrite(Motor3_Ausgaenge[i], LOW);
     }
  }
  //Motor3 Rechtsdrehung
  if(Direction3 == 1){
    for(int i=0; i<= Steps3; i++){
      for(int j=0; j<=7; j++){
        for(int k=0; k<=3; k++){
          digitalWrite(Motor3_Ausgaenge[k], StepValues[j][k]);
        }
        delay(Speed3);
      }
    }
   Motor3Move(Motor3Stop, 0,0);
   }
  //Motor3 Linksdrehung
  if(Direction3 == 2){
    for(int i=0; i<= Steps3; i++){
      for(int j=0; j<=7; j++){
        for(int k=0; k<=3; k++){
          digitalWrite(Motor3_Ausgaenge[k], StepValues[j][3-k]);
        }
        delay(Speed3);
      }
    }
  Motor3Move(Motor3Stop, 0, 0);
  }
}

jak_zim:
... und habe deshalb dementsprechend wenig Ahnung :confused:

Für den Arduino mag das zutreffen, aber Dein erstes Programm ist das nicht! Da muß ich meine Aussage, "vergiß delay" mal auf ein höheres Niveau heben.

Du programmierst ein Echtzeitsystem, das kein Multitasking kennt, weshalb Du selbst dafür sorgen mußt, die Aufgaben auf Zeitscheiben zu verteilen. Ein Schritt für Motor1, einer für Motor2, einer für Motor1 ...

Dafür ist blockadearmes Programmieren notwendig. Du hast delay in drei geschachtelten for-Schleifen versteckt, das ist blockierendes Programmieren.

Wenn Du hier im Forum nach "agmue Anleitung" suchst, findest Du auch ein Ampelbeispiel, wo die Ampel unabhängig von einem blinkenden Blaulicht agiert.

Mit der Bibliothek AccelStepper könntest Du Dir, wie schon erwähnt, die Arbeit erleichtern. Aber sportlicher Ergeiz, es ohne zu probieren, ist auch ok. Ich habe es auch erst ohne Bibliothek realisiert (Beispiel Kohlekran).

Du "denkst" in Umdrehungen und Programmierst jede Umdrehung oder Drehzahl seperat.

In diesem Fall musst du aber in Schritt denken. Jeder Schritt eines Motors ist für dich ein Programmdurchlauf. Dabei muss man Speed aus der Zeit berechnen. zB sich ausrechnen wie lange ein Step dauert. Du willst sagen wir 60U/min dann hast du 1U/sec und bei einem Motor mit 1,8° folglich 200Steps.

Nehmen wir an, das es ein Impuls/Pauseverhältniss von 50% ist so ist dein Impuls
(1 Sec/ (1U/sec * 200 Steps))/2 lang. Das sind (1/200)/2 also 1/400 oder 2,5msec. Hier ist dann auch schon grenze von Zeitauswertung in Millisekunden.

Du musst also nun die Zeit dir merken, an der du startest und den Motor einschalten, das selbe für Motor 2 und 3. Wenn die zeit rum ist, Motor ausschalten, halte die Reihenfolge ein, damit jeder die selbe verzögerung im programm hat.

Und danach zählst du den Step (step1++) bis die Anzahl steps erreicht ist. Viel schneller wird es mit dem Arduino wohl auch nicht gehen auf diesem Weg.

Soll es schnelle werden, musst du dich mit Timer und Interrupt beschäftigen oder den Code extrem optimieren, so das du auch bei micros() noch genug programmdurchläufe hast.

chefin:
Du musst also nun die Zeit dir merken, an der du startest und den Motor einschalten, das selbe für Motor 2 und 3. Wenn die zeit rum ist, Motor ausschalten, halte die Reihenfolge ein, damit jeder die selbe verzögerung im programm hat.

Beim DC-Motor schaltet man ein und aus für die Geschwindigkeit (PWM). Beim Schrittmotor wird die H-Brücke unterschiedlich angesteuert, um die Wicklungen des Schrittmotors unterschiedlich zu bestromen.

Anstelle "einschalten" würde ich daher "den ersten Schritt aus StepValues ausgeben", anstelle "ausschalten" "den nächsten Schritt aus StepValues ausgeben" formulieren.

Einverstanden?

Die Zeiten würde ich mit micros() berechnen, das hatte ich in #8 nicht erwähnt. Zwei Schrittmotoren sind für einen UNO zeitlich recht ambitioniert. Bei nur einem Motor hat bei mir ein Serial.print() schon gestört.

http://www.airspayce.com/mikem/arduino/AccelStepper/index.html

Ich vermute ja fast, dass es auf das Ansteuern einer CNC-Maschine mit 3 Achsen (=3 Motoren) hinauslaufen soll, oder?

Wenn ja, dann kannst du alternativ zur Verwendung der AccelStepper library, die dir das ganze Timing für mehrere Motoren abnimmt, anstelle alles selbst zu programmieren, auch direkt mit GRBL arbeiten.

Da kannst du dann vom PC/laptop o.ä. direkt via USB den G-Code an den Arduino senden, der dann diesen in Pulse für die Schrittmotoren umsetzt und nebenbei auch in der Lage ist, angeschlossene Begrenzungsschalter auszuwerten.

Jo, habe wieder mal ungeschickt formuliert, natürlich Step weiter schalten gemeint und nicht ein/aus.

Inzwischen mal versuche gemacht.

int LEDpin3 = 12;
int LEDpin2 = 11;
int LEDpin1 = 10;
unsigned long Zeit1, Zeit2, Zeit3;
bool Status1, Status2, Status3;
int Frequenz = 100;

void setup() {
  // put your setup code here, to run once:
pinMode(LEDpin1, OUTPUT);
pinMode(LEDpin2, OUTPUT);
pinMode(LEDpin3, OUTPUT);
Zeit1 = micros();
Zeit2 = micros();
Zeit3 = micros();
}

void loop() {
  // put your main code here, to run repeatedly:

if ((Zeit1 + Frequenz) < micros()){
  Zeit1 = micros(); 
  if (Status1){Status1 =LOW;}
  else {Status1 = HIGH;}
  }
if ((Zeit2 + Frequenz + (Frequenz/2)) < micros()){
  Zeit2 = micros(); 
  if (Status2){Status2 =LOW;}
  else {Status2 = HIGH;}
  }
if ((Zeit3 + Frequenz + Frequenz) < micros()){
  Zeit3 = micros(); 
  if (Status3){Status3 =LOW;}
  else {Status3 = HIGH;}
  }
digitalWrite(LEDpin1, Status1);
digitalWrite(LEDpin2, Status2);
digitalWrite(LEDpin3, Status3);}

Spätestens bei 20kHz war schluss.

Dann noch einen anderen Code geschrieben

int LEDpin3 = 12;
int LEDpin2 = 11;
int LEDpin1 = 10;
unsigned long Zeit1, Zeit2, Zeit3;
bool Status1, Status2, Status3;
int Frequenz = 100;

void setup() {
  // put your setup code here, to run once:
pinMode(LEDpin1, OUTPUT);
pinMode(LEDpin2, OUTPUT);
pinMode(LEDpin3, OUTPUT);
Zeit1 = micros();
Zeit2 = micros();
Zeit3 = micros();
}

void loop() {
  // put your main code here, to run repeatedly:

digitalWrite(LEDpin3,HIGH);
digitalWrite(LEDpin3,LOW);
}

Hier komme ich auf 88,5khz. Schneller gehts wohl nicht. Warum eigentlich? Hätte doch etwas mehr erwartet.

Und superlustig wird es wenn man den Code modifiziert zum debuggen

Zeit1=millis();
digitalWrite(LEDpin3,HIGH);
digitalWrite(LEDpin3,LOW);
Serial.println(Zeit1); //bei 19200 baud

Jetzt bekommt man Impulsspitzen mit etwas mehr als 5µs(entspricht wieder ungefähr den 88KHz) und dann laaaaange nichts. Am Ende dann 274 Hz Frequenz angezeigt. Was passiert? Der Serial-Befehl blockiert solange bis alle bits geschrieben sind.

Bei 115200 Baud sind es dann 1,6KHz. Wobei die Impulse eben 5µs groß bleiben. Serial blockiert also den Ablauf ähnlich wie delay. Es kann also passieren, das etwas ohne Debug-code funktionieren würde, aber beim Debug nie sauber läuft.

Serial blockiert also den Ablauf ähnlich wie delay. Es kann also passieren, das etwas ohne Debug-code funktionieren würde, aber beim Debug nie sauber läuft.

Stimmt genau; diese Erfahrung durfte ich vor einem Jahr, als ich mich zum ersten Mal mit Arduino und Stepper-Motoren beschäftigt habe, auch machen; seitdem sind alle Serials tabu, wenn ein Stepper läuft.

Danke erstmal für die vielen Antoworten.

Umsetzen will ich in meiner Studienarbeit ein Kuka-Roboter. Im 1. Teil sollen einfach 3 Achsen gesteuert werden, also ein Programmdurchlauf immer wieder abgearbeitet werden.
Im 2. Teil soll dann ein Greifer am Roboterarm Dinge greifen und versetzen, eventuell mit einer Steuerung übers Smartphone etc.

Ich werde mal die verschiedenen Lösungen ausprobieren. Über Librarys kann ich die Programmierung irgendwie nicht "nachvollziehen", weshalb ich darauf eigentlich verzichten will.

Gruß Jakob

Ich hoffe es ist ok, wenn ich Deine Nachricht in Ausschnitten hier veröffentliche:

Werden bei dem Kranbeispiel die Motoren wirklich gleichzeitig gesteuert?

Nein, der hat nur einen Motor für die Drehung und einen Servo für Schaufel auf/ab.

Irgendwie schaff ich es nicht die Ampelsteuerung auf mein Problem „zu übertragen". Da immer nur einzelne Pins angesteuert werden und nicht ein kompletter Code hinter „einer Ansteuerung" liegt.

Eine LED an oder aus entspricht dem nächsten Schritt für den Motor. Die Schritte gibst Du zeitverzögert an den Motor. Diese Verzögerung entspricht der bei der LED.

Wie kann ich ... eine "Phase" nach vorne springen? ist das überhaupt möglich?

Die Bitfolge "0,0,0,1" ist die erste Phase, die Du an IN1 bis IN4 ausgibst. Dann etwas warten. Die Bitfolge "0,0,1,1" ist die zweite Phase, die Du an IN1 bis IN4 ausgibst. Und so fort. Nach der achten Phase geht es wieder von vorne los.

Bei Bedarf Lesestoff: Mehrdimensionale Arrays

Danke erstmal für die vielen Antoworten.

Umsetzen will ich in meiner Studienarbeit ein Kuka-Roboter. Im 1. Teil sollen einfach 3 Achsen gesteuert werden, also ein Programmdurchlauf immer wieder abgearbeitet werden.
Im 2. Teil soll dann ein Greifer am Roboterarm Dinge greifen und versetzen, eventuell mit einer Steuerung übers Smartphone etc.

Ich werde mal die verschiedenen Lösungen ausprobieren. Über Librarys kann ich die Programmierung irgendwie nicht "nachvollziehen", weshalb ich darauf eigentlich verzichten will.
@agmue: Werden bei dem Kranbeispiel die Motoren wirklich gleichzeitig gesteuert? Wenn ich den Code lese denk ich dass immer nur ein Motor läuft.
Irgendwie schaff ich es nicht die Ampelsteuerung auf mein Problem „zu übertragen“. Da immer nur einzelne Pins angesteuert werden und nicht ein kompletter Code hinter „einer Ansteuerung“ liegt.

Bin außerdem auf einen MEGA umgestiegen weil die Ausgänge für noch mehr Schrittmotoren beim UNO nicht ausreichen. Die H-Brücken werden separat mit 5V versorgt.

Gruß Jakob