Stepper-Motor-Ansteuerung mit dem Arduino und einem Multi Function Shield

Das Multi Function Shield, das ich ja bereits vorgestellt habe, bietet mit der Möglichkeit zur einfachen Anzeige von Daten auf seinem 4-fach-7-Segment-Display und den freien Anschlusspins eine gute Grundlage, Sensoren ausprobieren.

Heute soll mit dem MFS und einem Schrittmotor 28BYJ-48 5V inkl. Treibermodul der Umgang mit einem Steppermotor gezeigt werden.


Schrittmotor 28BYJ-48


Ein gewöhnlicher Elektromotor dreht sich, sobald Strom anliegt und hört mit dem Drehen auf, wenn der Strom wieder aufhört, eventuell dreht er aufgrund Trägheit noch ein Stückchen nach. Eine exakte Positionierung, z. B. eine exakte 360°-Drehung ist mit diesen nicht zu bewerkstelligen. Hier kommt der Schrittmotor ins Spiel. Mit diesem ist es möglich, den Motor in bestimmten Grad-Schritten zu drehen, daher auch der Name.

Darum findet man Schrittmotoren überall, wo eine exakte Positionierung nötig ist, z. B. bei Plottern, 3D-Druckern oder auch in CD-/DVD-Laufwerken. Sogar in Festplatte kamen sie einst zum Einsatz. Ich kann mich noch gut an meine erste Festplatte erinnern, eine Seagate ST-412 MFM 5.25 Zoll, doppelte Bauhöhe, über 2 kg Gewicht und mit 10 MegaByte (sic!) Speicherkapazität und einem großen Steppermotor, dem man von außen bei der Arbeit zuschauen konnte. Sie kam damals in meiner Mailbox in den späten 1980ern / frühen 1990ern-Jahren zum Einsatz. Die Positionierung wird im Falle des vorliegenden 28BYJ-48 durch vier Spulen erreicht, die im Inneren des Motors bei Nord, Ost, Süd und West angeordnet sind und die die elektromagnetische Polarität ändern können (süden zum Kern, Norden zum Kern). An der drehbarer Achse des Motors ist ein Permanentmagnet angebracht. Durch wechselseitiges Wechseln der Polarität der 4 Spulen wird der Kern, an dem die Achse befestigt ist, schrittweise fortbewegt. Für die genaue Funktionsweise empfehle ich den ensprechenden Wikipedia-Artikel.

Zur Schaltung der 4 Spulen werden 4 Kabel aus dem Motor herausgeführt plus einen für die Masse, macht 5. Diese münden in einem Stecker, der vertauschungssicher in die Treiberplatine mit einem ULN2003 passt.


Die Technisches Daten des Schrittmotor 28BYJ-48 5V sind:

Treiberplatine ULN2003


Welche Spule gerade geschaltet wird, lässt sich - vorausgesetzt die Geschwindigkeit ist nicht zu hoch - wunderbar an den 4 roten LEDs der Treiberplatine erkennen (auf dem Foto hinter dem weißen Stecker). Hier leuchtet jeweils die LEDs, dessen Steuerleitungen aktiv sind. Auch Beschleunigungen lassen sich hiermit sehr schön visualisieren.

Die Treiberplatine ist mit einem ULN2003 Chip ausgestattet, der die Ansteuerung des Schrittmotors bewerkstelligt. Dafür braucht er eine extra Spannungsversorgung, die an den mit mit + und - bezeichneten Pins angelegt wird und 5 Volt betragen sollte. Da Ströme zwischen 130 und 400 mA für die Motorsteuerung benötigt werden, wäre dies zu viel für die GPIO-Pins des Arduinos. Am einfachsten schließt man ein USB-Ladegerät an, indem man ein altes USB-Kabel opfert und an dessen Plus- und Minuspol am Ende eine 2-Pin-Header-Buchse anlötet. So lässt sich auch schön mit einem USB Power Monitor die Stromstärke anzeigen, die das Treibermodul verbraucht.

Der danebenliegende Jumper kann abgezogen werden, dann fließt kein Strom mehr. Zur Motorsteuerung muss er natürlich aufgesteckt sein.


Die Treiberplatine ist das Teil, dass wir an den Multi Function Shield anschließen. Dazu verfügt sie über die 4 Header-Pins IN1 bis IN4, die ich folgendermaßen an das MFS angeschlossen habe: Leider reichen die digitalen Eingänge am 3x4 Header nicht aus, so dass ich mir auch noch den Pin von GPIO aus dem 6-Pin-Header borgen musste (lila Kabel).

Damit wäre die Hardware komplett verdrahtet.



Bibliothek AccelStepper


Fehlt nur noch die Software zur Ansteuerung. Für den 28BYJ-48 empfiehlt sich die Library AccelStepper, die damit sehr gut läuft.

Sie kann ganz einfach über den Bibliotheksverwalter der Arduino-IDE installiert werden.

Sie bietet folgende wichtige Funktionen - bezüglich des Stromverbrauchs sind einige Stolperstricke zu beachten:



Funktion / BeschreibungParameterBeispiel
Konstruktor. Instanziiert ein Stepper-Objekt zur weiteren Verwendung. So können mehrere Steppermotoren parallel verwendet werden. AccelStepper::AccelStepper ( uint8_t interface = AccelStepper::FULL4WIRE, uint8_t pin1 = 2, uint8_t pin2 = 3, uint8_t pin3 = 4, uint8_t pin4 = 5, bool enable = true ) Für den 28BYJ-48 5V ist richtig: AccelStepper stepper1(HALF4WIRE, motorPin1, motorPin3, motorPin2, motorPin4);
currentPosition() : gibt die gegenwärtige Position in Schritte zurücklong AccelStepper::currentPosition ( ) Position in Grad berechnen: float grad = stepper1.currentPosition() * GradProSchritt;
setCurrentPosition() : hiermit kann man den Positions-Zähler zurücksetzen.void AccelStepper::setCurrentPosition ( long position ) Werte in einer 360°-Skala halten:int schritte = stepper1.currentPosition(); float grad = schritte * GradProSchritt; if (grad >= 360) stepper1.setCurrentPosition(schritte-4096); if (grad <= -360) stepper1.setCurrentPosition(schritte+4096);
disableOutputs() : Schaltet nach einer Bewegung die Motorsteuerung wieder aus. Wird dies nicht gemacht, zieht die Treiberplatine kontinuierlich 140 mAvoid AccelStepper::disableOutputs ( ) Motorsteuerung aus und Strom sparen:stepper1.disableOutputs();
setSpeed() : Setzt die Geschwindigkeit, mit dem sich der Motor bewegt in Schritte pro Sekunde. Sollte nicht mehr als 2000 betragen, da es sonst zu Störungen kommt.void AccelStepper::setSpeed ( float speed ) Maximale Geschwindigkeit: stepper1.setSpeed(2000);
setMaxSpeed() : Setzt die maximale Geschwindigkeit, mit dem sich der Motor bewegt. Sollte nicht mehr als 2000 betragen, da es sonst zu Störungen kommt.void AccelStepper::setMaxSpeed ( float speed ) Maximale Geschwindigkeit: stepper1.setMaxSpeed(2000);
setAcceleration() : Setzt die Beschleunigung, mit der der Motor beschleunigt und abgebremst wird. Ermöglicht zusammen mit setSpeed eine schnellere oder genauere Positionierung. Je langsamer, desto sicherer ist die Positionierung. Eine Volldrehung benötigt mit den Werte 2000, 2000, 1000 etwa 4 Sekunden.void AccelStepper::setAcceleration ( float acceleration ) Moderate Beschleunigung: stepper1.setAcceleration(1000);
move() : legt die Bewegung relativ zur aktuellen Position (in Schritten, positiv: im Uhrzeigersinn, negativ: entgegen des Uhrzeigersinns) fest. Eine Komplettdrehung sind 4096 Schritte. Die Bewegung wird ausgeführt wenn run() oder runToPosition() aufgerufen wird.void AccelStepper::move ( long relative ) Volle Drehung und dann stromsparen: stepper1.move(4096); stepper1.runToPosition(); stepper1.disableOutputs();
runToPosition() : Führt die durch move() festgelegte Bewegung aus. Blockt das Programm und wartet, bis durchgeführt.void AccelStepper::runToPosition ( ) siehe oben
run() : Führt die mit move() festgelegte Bewegung asynchron aus, muss dazu aber immer wieder in der Loop-Schleife aufgerufen werden.
boolean AccelStepper::run ( ) Bei einem bestimmten Ereignis durch move() die Bewegung festlegen und durch wiederholten Aufruf durch run() im Loop() ausführen. Wenn der Motor fertig ist, stromsparen: if (...) stepper1.move(4096);
... boolean isRunning = stepper1.run(); if (!isRunning) stepper1.disableOutputs();
stop() : Motor so schnell wie möglich stoppen. void AccelStepper::stop ( ) Motor bei einem Ereignis stoppen: if (NotAus) stepper1.stop();

Programm

Das Programm simuliert die Schritte eines Sekundenzeigers, das heißt, jeweils um 6 Grad (360° / 60 Einheiten).

Mit dem rechten Taster bewegt man sich eine Sekunde vorwärts, mit dem linken Taster eine Sekunde rückwärts. Der mittlere Taste stellt der Zeiger wieder auf die Null-Position.

Die Siebensegmentanzeige des Multi-Function-Shields zeigt die Sekunden von 0 bis 59 an. Wer lieber die Grad-Zahl angezeigt haben möchte, nimmt das "/6" im Code raus. Natürlich sind auch andere Schrittweiten durch Programmanpassung möglich.

Ich habe ein kleines Video gemacht, um die Funktionen und die Genauigkeit zu demonstrieren. Man sieht hierbei auch schön die LEDs der Treiberplatine, die anschaulich die Spulenansteuerung zeigt. Um diesen Effekt noch zu erhöhen, kann man die Geschwindigkeiten im Code herabsetzen.



Source-Code

//////////////////////////////////////////////////////// // (C) 2018 by Oliver Kuhlemann // // Bei Verwendung freue ich mich über Namensnennung, // // Quellenangabe und Verlinkung // // Quelle: http://cool-web.de/arduino/ // //////////////////////////////////////////////////////// #include <TimerOne.h> // für Timings und Interrupts #include <MultiFuncShield.h> // API für das Multi Function Shield #include <AccelStepper.h> // API für den Step Motor 28BYJ-48 5V #define HALF4WIRE 8 #define PinMotor1 2 // IN1 on the ULN2003 driver #define PinMotor2 5 // IN2 on the ULN2003 driver #define PinMotor3 6 // IN3 on the ULN2003 driver #define PinMotor4 9 // IN4 on the ULN2003 driver #define Schritte360Grad 4096 #define GradProSchritt 0.087890625 // 360° / 4096 Schritte // Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper with 28BYJ-48 AccelStepper stepper1(HALF4WIRE, PinMotor1, PinMotor3, PinMotor2, PinMotor4); void setup() { Timer1.initialize(); // TimerOne initialisieren MFS.initialize(&Timer1); // Multi Function Shield initialisieren MFS.write("step"); delay (2000); MFS.write("secs"); delay (2000); MFS.write(""); // Geschwindigkeitseinstellungen stepper1.setMaxSpeed(2000); stepper1.setSpeed(2000); stepper1.setAcceleration(1000); } void loop() { byte btn = MFS.getButton(); if (btn == BUTTON_1_PRESSED && !stepper1.isRunning()) { MFS.beep(1); stepper1.move(-6/GradProSchritt); } else if (btn == BUTTON_3_PRESSED && !stepper1.isRunning()) { MFS.beep(1); stepper1.move(6/GradProSchritt); } else if (btn == BUTTON_2_PRESSED && !stepper1.isRunning()) { MFS.beep(1); stepper1.moveTo(0); } boolean isRunning = stepper1.run(); if (!isRunning) stepper1.disableOutputs(); int schritte = stepper1.currentPosition(); float grad = schritte * GradProSchritt; if (grad >= 360) stepper1.setCurrentPosition(schritte-Schritte360Grad); if (grad <= -360) stepper1.setCurrentPosition(schritte+Schritte360Grad); MFS.write(grad/6,0); } Wichtig ist die Zeile if (!isRunning) stepper1.disableOutputs();, die dafür sorgt, dass die Motorsteuerung ausgeschaltet wird, sobald die Bewegung beendet ist. Lässt man diese Zeile weg, dann bewegt sich der Motor zwar nicht, es werden aber trotzdem ca. 140 mA vergeudet. Das erwärmt den Motor und saugt eine etwaige Batterie unnötigerweise schnell leer.

Ein eigenes Programm sollte man deshalb auch mit dem Blick auf ein angeschlossenes Amperemeter testen.