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:

  • 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.
Zwei Arten von Schrittmotoren
Zwei Arten von Schrittmotoren

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]

Wave-Drive

Um die anderen Modi aus zu probieren ändern Sie den Inhalt des ROM und passen Sie gegebenenfalls den Zähler an.

PhaseWave-Drive (weniger Strom)Full-Step (mehr Kraft)Half-Step (mehr Schritte)
00b0001 = 10b0011 = 30b0001 = 1
10b0010 = 20b0110 = 60b0011 = 3
20b0100 = 40b1100 = 12 = 0xC0b0010 = 2
30b1000 = 80b1001 = 90b0110 = 6
40b0100 = 4
50b1100 = 12 = 0xC
60b1000 = 8
70x1001 = 9
Ausgabe im jeweiligen Modus pro Phase

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].

ULN2003 Blockschaltbild
ULN2003 Innenschaltung

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.

Simulation im Wave-Drive Betrieb
Simulation im Wave-Drive Betrieb

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.

Treiber L293D

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.

Simulation mit L293

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.

Schema des 28BYJ-48
Schrittmotor mit Treiberschaltung

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..

EingangFunduino SchrittmotoranschlußAndere Treibermodule
IN1OrangeBlau
IN2GelbPink
IN3PinkGelb
IN4BlauOrange
Verbindung Eingänge zu Schrittmotor
Treiberplatine mit ULN2003
Treiberplatine mit ULN2003

Anschluss an STM32

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

Verkabelung
Arduino-PinSTM32-PinSpulenfarbe
D3PB3Blau
D5PB4Pink
D4PB5Gelb
D10PB6Orange
Zuordnung STM32-Pin zu Spule

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.

Messung bei 100 Hz
Messung bei 100 Hz Wave-Drive

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 bei 550 Hz
Messung bei 550 Hz Wave-Drive

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

Messung bei 690 Hz Vollschritt
Messung bei 690 Hz Vollschritt

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

Messung bei 1350 Hz Halbschritt
Messung bei 1350 Hz Halbschritt

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

Skala für Schrittmotor
Skala für Schrittmotor

Mit Processing eine Skala für die Positionsanzeige erstellen.

Die Skala ist auf eine alte CD aufgeklebt.

Skala mit CD und Nullpunktdraht
Skala mit CD und Nullpunktdraht

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

Projekt Unterrichtsuhr mit Schrittmotor

Projektseite