3.3 🕺 Schrittmotor 🚧
Synopsis: [funduino.de/nr-15-schrittmotor] [de.wikipedia.org/wiki/Schrittmotor] [https://mezmedia.de/technische-informatik/digitaltechnik/2-4-schaltwerke-bauen/#schrittmotor] [🔗 youtube.com/watch?v=draBqtk7BKY]
In einer idealen Welt passt alles einfach zusammen: Prima Video erklärt wie der im Unterricht verwendete Schrittmotor aufgebaut ist und funktioniert [Sehr genaue Beschreibung: 🔗 28BYJ-48 Schrittmotor für Profis auf Youtube]. SuS können mit Digital die drei Ansteuerungsarten ausprobieren (siehe unten: Simulation mit Digital). Der Anschluß an den µC klappt einfach mit günstiger Hardware [funduino.de/nr-15-schrittmotor]..
Ausgehend vom Video: Leider dreht der Schrittmotor in Digital in die umgekehrte Richtung und beim Funduino-Treiber ist die Anschlussreihenfolge gespiegelt. Für Menschen wie mich, die sich durch solche Widersprüche bis zur Aufklärung verwirren lassen bedeutet das Mehraufwand…
Funktionsweise Schrittmotor
Lesen: [🔗 de.wikipedia.org/wiki/Schrittmotor] [🔗 rn-wissen.de/wiki/index.php/Schrittmotoren]
Ich habe bislang noch kein für mich stimmiges (nicht nerviges) Video für den Einstieg gefunden, falls jemand eines findet, bitte mir schicken..
Bisher beste Einsteiger-Videos:
- [So funktionieren Schrittmotoren, einfach erklärt!: 🔗 https://www.youtube.com/watch?v=aysx3r_nPCo]
- [Schrittmotor (vereinfacht): 🔗 youtube.com/watch?v=draBqtk7BKY]
- Bipolar: Stromrichtung der Spulen wird umgekehrt, braucht H-Brücke, Beschaltung aufwendiger.
- Unipolar: Spulen haben Mittenanzapfung, jeweils eine Halbspule wird unter Strom gesetzt, einfachere Beschaltung.

Simulation mit Digital
Mit Digital können bipolare und unipolare Schrittmotoren simuliert werden. Der Zähler gibt die Phase vor, das ROM die Ausgabe an den Schrittmotor.
Aufgabenstellung mit Digitaltechnik Schrittmotor: [https://mezmedia.de/technische-informatik/digitaltechnik/2-4-schaltwerke-bauen/#schrittmotor]
Um die anderen Modi aus zu probieren ändern Sie den Inhalt des ROM und passen Sie gegebenenfalls den Zähler an.
| Phase | Wave-Drive (weniger Strom) | Full-Step (mehr Kraft) | Half-Step (mehr Schritte) |
|---|---|---|---|
| 0 | 0b0001 = 1 | 0b0011 = 3 | 0b0001 = 1 |
| 1 | 0b0010 = 2 | 0b0110 = 6 | 0b0011 = 3 |
| 2 | 0b0100 = 4 | 0b1100 = 12 = 0xC | 0b0010 = 2 |
| 3 | 0b1000 = 8 | 0b1001 = 9 | 0b0110 = 6 |
| 4 | – | – | 0b0100 = 4 |
| 5 | – | – | 0b1100 = 12 = 0xC |
| 6 | – | – | 0b1000 = 8 |
| 7 | – | – | 0x1001 = 9 |
Treiberschaltungen liefern größere Ströme
Um die Spulen mit Strom zu versorgen sind deutlich größere Ströme notwendig als die Ausgänge der Digitalgatter bzw. Port-Pins liefern können. Es gibt Treiberschaltungen dafür.
Unipolar-Ansteuerung mit ULN2003
Beim Unipolar-Schrittmotor reichen 4 Transistoren. Beliebt ist dafür der [🔗 Datenblatt ULN2003].


Bei induktiven Lasten (Spulen) sind Schutzdioden (Freilaufdioden) notwendig um die in den Spulen gespeicherte Energie beim Abschalten der Spulen auf zu nehmen, sonst würde die hohe Induktionsspannung die Transistoren beschädigen. [🔗 de.wikipedia.org/wiki/Schutzdiode]
Simulation mit ULN2003-Treiber
Hier eine Simulation mit unipolarerm Schrittmotor und ULN2003-Treiber (n-Kanal-MOS- statt npn-Transitoren, die Freilaufdioden wurden weg gelassen).
Der gemeinsame Mittenanschluß wurde mit +5V statt GND verbunden, damit die Spulen gegen GND geschaltet werden können. Dabei ändert sich die Drehrichtung, da die erzeugten Magnetfelder auch umgekehrt sind.
Bipolar-Ansteuerung mit L293D-Treiber
Beim Bipolar-Schrittmotor müssen die Eingänge zwischen Vs und GND geschaltet werden können, die Treiberschaltung ist dabei aufwändiger weil die Ausgänge Strom liefern und aufnehmen müssen (Push-Pull).
Ein günstiger Treiber ist der L293D:
[🔗 st.com/resource/en/datasheet/l293d.pdf].
Wieder sind Schutzdioden wegen den Induktionsspannungen verbaut.
Leider hat der Baustein eine recht hohe Ruhestromaufnahme ca. 35mA, die bei Batterie betriebenen Schaltungen ungünstig ist.

Simulation mit L293D-Treiber
Hier eine Simulation mit bipolarem Schrittmotor und L293D-Treiber (n-Kanal-MOS- statt npn-Transitoren, die Freilaufdioden wurden weg gelassen).
Zum Verständnis habe ich die Ausgangstransitoren der Treiber dargestellt.
Auf dem Funduino Expansion Board für NUCLEO STM32 sind 2 L293D Bausteine verbaut und an die PortPins PC0..PC7 angeschlossen. Die Enable-Eingänge sind mit +5V verbunden.
Schrittmotor 28BYJ-48 mit Treiber-Platine von Funduino
Synopsis: [🔗 Schrittmotor 28BYJ-48 mit ULN2003 Treiberplatine] [Beschreibung: 🔗 cookierobotics.com/042/] [Sehr genaue Beschreibung: 🔗 28BYJ-48 Schrittmotor für Profis auf Youtube] [🔗 Datenblatt Reichelt]
Der 28BYJ-48 ist ein Unipolar-Schrittmotor mit einem Getriebe. Unipolar bedeutet, dass die Spulen eine Mittelanzapfung haben und der Betrieb ohne Spannungsumkehr möglich ist. Die 4 Halbspulen sind mit einer gemeinsamen Mitte (rote Leitung) verbunden und haben einen Widerstand von ca. 25Ω. Eine Stromberechnung für 5V: 5V/25Ω = 200mA, im Voll- und Halbschrittbetrieb sind oft 2 Spulen unter Strom daher ist mit 400mA Strombedarf zu rechnen. Die aufgenommene Leistung ist dann 5V*400mA = 2W, das Ding wird “heiß”. Der Schrittmotor braucht 32 Volltakte für eine Umdrehung, das Getriebe übersetzt 64, d.h. für eine volle Umdrehung der Ausgangswelle braucht es im Vollschrittbetrieb 2048 und im Halbschrittbetrieb 4096 Takte.


Treiberplatinen mit ULN2003
Der Treiberbaustein ULN2003 [🔗 Datenblatt ULN2003] hat 7 Treiberstufen, im Chip sind Schutzdioden eingebaut, die die Induktionsspannungen der Spulen abfangen sollen, sie sind mit dem com-Anschluss verbunden, der mit Versorgungsspannung des Schrittmotors verbunden ist. Über einen Jumper kann VCC vom µC-Board auf die Versorgungsspannung gebrückt werden. Bei der Funduinoversion sind die Eingänge IN1..IN7 auf die Ausgänge A..G geschaltet. Die Eingänge IN1..IN4 auf den Verbinder zum Schrittmotor. Die Reihenfolge ist beim Funduinomodul umgekehrt, kann zu anderer Drehrichtung führen..
| Eingang | Funduino Schrittmotoranschluß | Andere Treibermodule |
|---|---|---|
| IN1 | Orange | Blau |
| IN2 | Gelb | Pink |
| IN3 | Pink | Gelb |
| IN4 | Blau | Orange |

Anschluss an STM32
Teile für Fritzing laden: [Fritzing Schrittmotor] [Fritzing Treiberschaltung] [Fritzing Funduino-Treiber]

| Arduino-Pin | STM32-Pin | Spulenfarbe |
|---|---|---|
| D3 | PB3 | Blau |
| D5 | PB4 | Pink |
| D4 | PB5 | Gelb |
| D10 | PB6 | Orange |
Die Zuordnung wurde von mir so gewählt, dass die Anschlüsse in einem gemeinsamen Port GPIOB beim STM32 nacheinander landen.
Testprogramm
void setup() {
pinMode(PB3,OUTPUT);
pinMode(PB4,OUTPUT);
pinMode(PB5,OUTPUT);
pinMode(PB6,OUTPUT);
pinMode(PC13,INPUT); // User Button
Serial.begin(9600); // Serielle Schnittstelle starten und Baudrate festlegen
}
unsigned char motorCW[]={0b0001,0b0010,0b0100,0b1000}; // Wave-Drive (Vollschritt weniger Strom)
//unsigned char motorCW[]={0b0011,0b0110,0b1100,0b1001}; // Full-Step (Vollschritt normal)
//unsigned char motorCW[]={0b0001,0b0011,0b0010,0b0110,0b0100,0b1100,0b1000,0b1001}; // Half-Step
int frequenz = 10;
void loop() {
if(!digitalRead(PC13)){
frequenz += 10;
Serial.printf("Frequenz: %d\n",frequenz);
while(!digitalRead(PC13));
}
for (int i=0;i<sizeof(motorCW);i++){ // alle Phasen durchgehen
GPIOB->ODR= motorCW[i]<<3; // Zurechtschieben
if(frequenz<100)delay(1000/frequenz); // niedrige Frequenzen
else delayMicroseconds(1000000/frequenz); // hohe Frequenzen
}
}
Stimmt die Frequenz bei höheren Werten? Verbessern mit Timer-ISR? Bei welcher Frequenz ist der Motor in dem jeweiligen Modus überfordert?
Messungen mit Oszi
Mit dem Analog Discovery Studio habe ich mehrere Messungen gemacht. Orange ist der Eingang IN1 in das Treibermodul und Blau ist der Ausgang A zum Schrittmotor. Die erste Messung erfolgte mit 100Hz Umschaltfrequenz, da nur jede 4. Phase an einer Spule ankommt zeigt die Frequenzmessung ca. 25Hz an. Dem Elektotechniker geht bei der blauen Kurve der Spule das Herz auf, ein wunderbares Induktions-Schauspiel.

Bei 550Hz war Schluß, danach drehte sich die Achse nicht mehr. Nach Messung wäre die Frequenz 137,36Hz*4 = 549,44Hz gewesen, passt also noch gut bei höheren Frequenzen, eine ISR-Lösung wäre nicht unbedingt nötig.

Messung mit Vollschritt: Bei 690Hz war Schluß. 172,29Hz*4=689,16Hz.

Messung mit Halbschritt: Bei 1350Hz war Schluß (/2=675Hz). 168,55Hz*8=1348,4Hz

Poti steuert Schrittmotorposition
Das Poti an A0 soll die Postion des Schrittmotors steuern. Der Schrittmotor hat im Vollschrittbetrieb 2048 Positionen, das Poti ergibt nach AD-Wandlung Werte von 0..1023. Ein schlichter Draht an PA8 (D7) gibt bei Kontakt mit dem Zeiger (GND) die Nullposition. Hier eine Testsoftware zum experimentieren.
#include <Wire.h> // Wire Bibliothek einbinden
#include <LiquidCrystal_PCF8574.h>
#define P_NULLSCHALTER PA8 // D7 Kontakt bei Nullpunkt
#define P_USERB PC13
#define P_POTI A0 // Poti an PA0
#define NULLSCHALTER_POSITION 0 // Position des Nullpunktschalters
LiquidCrystal_PCF8574 lcd (0x27); // LCD-Adresse auf 0x27 für 16 Zeichen und 2 Zeilen ein
void setup() {
lcd.begin(16, 2); // LCD initialisieren
lcd.clear(); // Displaypuffer löschen
lcd.setBacklight(255); // Hintergrundbeleuchtung einschalten
lcd.setCursor(0,0); // Cursor auf erstes Zeichen, erste Zeile setzen
pinMode(PB3,OUTPUT); // D3
pinMode(PB4,OUTPUT); // D5
pinMode(PB5,OUTPUT); // D4
pinMode(PB6,OUTPUT); // D10
pinMode(P_NULLSCHALTER,INPUT_PULLUP); // D7 für Nullpunkt
pinMode(PC13,INPUT); // User Button
Serial.begin(9600); // Serielle Schnittstelle starten und Baudrate festlegen
}
unsigned char motorCW[]={0b0001,0b0010,0b0100,0b1000}; // Wave-Drive (Vollschritt weniger Strom)
//unsigned char motorCW[]={0b0011,0b0110,0b1100,0b1001}; // Full-Step (Vollschritt normal)
//unsigned char motorCW[]={0b0001,0b0011,0b0010,0b0110,0b0100,0b1100,0b1000,0b1001}; // Half-Step
int frequenz = 200; // in Herz
int position = 2048; // maximale Schrittzahl
int phase = 0; // Schrittmotorphase
enum zustandstyp {NULLPUNKT_ENTRY,NULLPUNKT,POTI_LESEN,NULLPUNKT_CHECK,FEHLER}; // definiere Aufzählungstyp
enum zustandstyp zustand = NULLPUNKT_ENTRY; // Definiere und initialisiere Variable
int ausgebenMotor(int n){ // Schrittmotor auf Position n fahren
if(n==position){ // wenn Position stimmt
return 0; // fertig
}
GPIOB->ODR= motorCW[phase]<<3; // Einschalten: Zurechtschieben und ausgeben
delayMicroseconds(1000000/frequenz); // warten bis Schritt vollzogen
go:
if(n>position){
position++;
phase++;
if(phase>=sizeof(motorCW))phase=0;
}
else if(n<position){ // n<position
if(!digitalRead(P_NULLSCHALTER)){ // wurde Endschalter getroffen?
GPIOB->ODR=0; // abschalten
return position;
}
position--;
if(phase==0)phase=sizeof(motorCW)-1;
else phase--;
}
GPIOB->ODR= motorCW[phase]<<3; // Zurechtschieben und ausgeben
delayMicroseconds(1000000/frequenz); // warten bis Schritt vollzogen
if(n==position){
GPIOB->ODR=0; // abschalten
return 0; // fertig
}
goto go;
}
void loop() {
int poti;
switch(zustand){
case NULLPUNKT_ENTRY: // Nach Einschalten Nullpunkt finden
position = 2048; // Maximale Umdrehung
lcd.setCursor(0,0);
lcd.print("Nullpunkt finden");
zustand = NULLPUNKT;
break;
case NULLPUNKT: // Nullpunkt finden
ausgebenMotor(0); // Motor auf Nullpunkt steuern
if(position==0){ // kein Nullpunkt gefunden
zustand = FEHLER;
break;
}
position=NULLSCHALTER_POSITION; // Nullpunkt soll 0 Schritte von Schalter entfert sein
if(NULLSCHALTER_POSITION>0){
ausgebenMotor(NULLSCHALTER_POSITION); // auf Minimum steuern
}
else ausgebenMotor(0); // auf Nullpunkt steuern
zustand = POTI_LESEN;
break;
case POTI_LESEN:
if(!digitalRead(P_USERB)){ // Nullpunkt-Test auslösen
zustand = NULLPUNKT_CHECK;
break;
}
poti = 0;
for(int i=0;i<10;i++){ // 10 Messungen machen
poti += analogRead(A0);
delay(10);
}
poti /= 10;
if(NULLSCHALTER_POSITION>0) poti += NULLSCHALTER_POSITION;
lcd.setCursor(0,1);
lcd.printf("S %4d I %4d",poti,position); // Soll- Istposition
if(ausgebenMotor(poti)){ // falls Endpunktschalter berührt
lcd.setCursor(0,0);
lcd.printf("Endschalter %4d",position);
}
break;
case NULLPUNKT_CHECK: // Überprüfen ob Nullpunkt noch stimmt
ausgebenMotor(-200); // Gegen Nullschalter fahren
lcd.setCursor(0,0);
lcd.printf("Nullpunkt %4d ",position);
position = 2048;
zustand = NULLPUNKT;
break;
case FEHLER:
lcd.setCursor(0,1);
lcd.print("Nullpunktfehler!");
break;
}
}
Erkenntnisse: Bei höheren Frequenzen verstellt sich die Position. Das Zahnradspiel des Schrittmotors macht sich bemerkbar.
ToDo: Bild und Anschlussschema.
Anzeigeskala mit Processing

Mit Processing eine Skala für die Positionsanzeige erstellen.
Die Skala ist auf eine alte CD aufgeklebt.

Skala als PDF:
Download Processing: https://processing.org
Verbesserung: Der Nullpunktdraht sollte unterhalb des Nullpunkts sein.
Processing-Code
import processing.pdf.*;
boolean pdfAufzeichnen=true; // bei True wird PDF erzeugt
float achseX,achseY; // Position der Schrittmotorachse
float skala0 = -PI; // Winkel bei 0
int skalaMin = 0; // kleinster Skalenwert
int skalaMax = 2000; // größter Skalenwert
int strichAnf = 88; // Radius
int strichLaenge = 8; // Länge eines Strichs
int skalaRadius = 240; // äusserer Rand der Skala zum Ausschneiden
int sekunde =0;
void setup() {
size(580,400);
achseX=width/2;
achseY=height/2;
pixelDensity(1); // damit es beim PDF klappt, sonst wird nur 1/4 dargestellt
if(pdfAufzeichnen){
beginRecord(PDF, "Skala.pdf"); // Aufzeichnen und als PDF speichern
background(255); // weisser Hintergrund
stroke(0);
rect(0,0,width,height);
PFont font;
font = createFont("Arial", 10);
textFont(font);
textAlign(CENTER);
fill(0);
for(int i=1;i<=width/20;i++){ // Kalibrationshilfe für Ausdruck
line(i*20,height,i*20,height-7);
text(i,i*20,height-10);
}
maleLogo();
maleSkala();
endRecord();
}
}
void maleSkala(){
float winkel;
stroke(0); // schwarz
fill(#00ff00); // grüne Füllung
circle(achseX,achseY,12); // 6mm Kreis für Achsenmitte
PFont skalaFont = createFont("Arial", 8); //Chalkboard
textFont(skalaFont);
fill(#0000AA); // Blau
textAlign(CENTER,CENTER);
for(int i =skalaMin;i<=skalaMax; i+=20){
winkel = i*2*PI/2048;
if(i%100==0){
stroke(#0000ff);
strokeWeight(2);
text(i,(achseX-(strichAnf+strichLaenge+12)*cos(winkel)),(achseY-(strichAnf+strichLaenge+16)*sin(winkel)));
}
else{
stroke(#ff0000);
strokeWeight(1);
}
line(achseX-strichAnf*cos(winkel),achseY-strichAnf*sin(winkel),achseX-(strichAnf+strichLaenge)*cos(winkel),achseY-(strichAnf+strichLaenge)*sin(winkel));
}
stroke(200);
strokeWeight(1);
noFill();
arc(achseX,achseY,skalaRadius,skalaRadius,-PI,PI); // Umrandung zum Ausschneiden
}
void maleLogo(){
PFont font;
fill(0);
font = createFont("Arial", 6);
textFont(font);
text("© 2025 Oliver Mezger MezMedia.de CC BY-SA 4.0", width/2, height-160);
}
void draw() {
if(sekunde!=second()){ // eine Sekunde ist rum
sekunde=second();
background(255);
maleSkala();
maleLogo();
}
}
void mousePressed() {
endRecord();
exit();
}
🚧 Aufgaben dazu
Ersetze ausgebenMotor(int n) mit delayMicroseconds() (ist blockierend damit es beim Fahren keine Störungen, Ruckler gibt) durch TimerISR-Lösung.
Ist die Wiederholgenauigkeit beim Anfahren einer Position akzeptabel?
Untersuche systematisch welches die maximale Frequenz für fehlerfreien Betrieb ist:
- Wave-Drive mit und ohne Stromabschaltung
- Full-Step mit und ohne Stromabschaltung
- Half-Step mit und ohne Stromabschaltung
