Vorschub Schrittmotor und LCD

Hallo Forum!

Ich bin aktuell gerade dabei einen Vorschub für meine Fräse zu implementieren und verwende dafür einen vorgefertigten Sketch den ich etwas adaptiert habe. Aktuell stehe ich vor dem Problem, dass ich gerne ein LCD hätte, welches den aktuell Potiwert ausgibt, damit ich etwas erahnen kann um welchen Vorschub es sich handelt bzw. das ganze etwas besser ins Gefühl zu bekommen. Ich verwende ein 20x4 I2C Display, da ich ansonstent mit dem Nano zu wenige Ports habe bzw. ich ein “Handpanel” verwende, zu viele Adern des Kabels benötigen.

Mein Problem aktuell: lcd.clear() braucht viel zu lange um das Display zu löschen um nachher sauber zu schreiben, auch verlängert es unnötig die die Stepper Steuerung, sodass ich keine vernünftige Geschwindigkeit zusammen bekomme. Hat hier jemand eventuell einen Vorschlag wie man das lösen könnte? Ich hab es schon probiert mit überschreiben anstatt zu löschen aber das hat mein Problem nicht gelöst. Ist es überhaupt möglich I2C LCD und Schrittmotor zu realisieren? Ich steh momentan wirklich auf dem Schlauch wie ich das lösen könnte :frowning:

Hier ist mein Code:

// Parameter und Definitionen nach Gusto anpassen
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display

// Ausgangs-Pins
#define READY   13                            // Bereitschaft
#define STEP    10                            // Pin fuer Step-Impuls
#define DIRP    9                            // Pin fuer Richtung
#define ENABLE   3                            // Pin fuer Enable fuer die Stepper-Endstufe
#define LEFTFAST 5                            // Taster Links
#define RIGHTFAST 4                           // Taster Rechts
// Eingangs-Pins
#define POTI    A3                            // Analogeingang fuer Poti
#define SWITCH  7                            // Geschwindigkeits-Wahlschalter
#define STOP 8                                // Endschalter      

// Poti-Paramter
#define Puse    90                            // nutzbarer Drehwinkel des Potis in % (max. 100)
#define Ruhe    20                            // Ruhezone in % (min. 1)
#define MITTE  512                            // ADC-Wert Poti in Mittelstellung
                                              // sollte nicht allzu weit von 512 abweichen
// Schrittfrequenz
#define Fmin     4                            // Minimale Schrittfrequenz - nicht kleiner als 1
#define Fmax  4200                           // Maximale Schrittfrequenz - nicht groesser als 4200
#define RFAKTOR  8                            // Reduktionsfaktor Slow-Mode

#define RICHTUNG 1                            // Richtungsumkehr: auf 0 oder 1 setzen je nach Wunsch                    

#define RELEASE  1                            // Schrittmotor "loslassen" in Ruhestellung? 0/1

// ab hier nichts mehr ändern

#define Pmin ((335544L * Ruhe) >> 16)         // Minwert vom Poti
#define Pmax ((335544L * Puse) >> 16)         // Maxwert vom Poti

int p, s, l;
int f = Fmax;
long d;

int aRead() {
  int v = analogRead(POTI) - MITTE;           // 0-1023 -> -512 .. +511
  v <<= 2;                                    // ganz einfache Glaettung
  v += l;
  v += l;
  v += l;
  v >>= 2;
  l = v;
  v >>= 2;
  return v;
}
  
void setup() {


  uint8_t i, r;
   pinMode(LEFTFAST, INPUT_PULLUP);
   pinMode(RIGHTFAST, INPUT_PULLUP);
  pinMode(READY, OUTPUT);                     // Bereitschaftsanzeige
  pinMode(STEP, OUTPUT);                      // Step
  pinMode(DIRP, OUTPUT);                      // Richtungs-Pin
  pinMode(ENABLE, OUTPUT);                    // Enable-Pin
  pinMode(SWITCH, INPUT);                     // Geschwindigkeits-Wahlschalter
  pinMode(STOP, INPUT_PULLUP);                // Endschalter
  digitalWrite(SWITCH, HIGH);                 // Pullup
  digitalWrite(ENABLE, LOW);                  // Enable 0
  while (--i) {                               // Warten bis sich der ADC stabilisiert
    aRead();
    delay(3);
    digitalWrite(READY, (i & 0x40) ? HIGH : LOW);
  }
  if (abs(l) > (Pmin * 4/5)) r++;
  while (r) {                                 // Warten bis Poti stabil in Ruhezone
    aRead();
    if (abs(l) < (Pmin * 4/5)) r++;
    delay(1);
    digitalWrite(READY, (i++ & 0x20) ? HIGH : LOW);
  }
  digitalWrite(READY, HIGH);                  // Bereit
}

void loop() {

   digitalWrite(ENABLE,LOW);
/* LCD aktualisieren mit Poti-Wert */
while(digitalRead(STOP)==0){      
    
  while(digitalRead(LEFTFAST)==0)
  {
     if(digitalRead(STOP)==0){
      digitalWrite(STEP, LOW); 
      digitalWrite(ENABLE, HIGH); 
      digitalWrite(DIRP,LOW);   // Richtungs-Pin fuer Stepper
      delayMicroseconds(45);
      digitalWrite(STEP, HIGH); 
      }
  }
    while(digitalRead(RIGHTFAST)==0)
  {
    if(digitalRead(STOP)==0){
      digitalWrite(ENABLE, HIGH);    
      digitalWrite(STEP, LOW);    
      digitalWrite(DIRP,HIGH);   // Richtungs-Pin fuer Stepper
      delayMicroseconds(50);
      digitalWrite(STEP, HIGH);  
    }  
  }

  
  p = aRead();                                // geglaetteten Potiwert holen
  digitalWrite(STEP, LOW);                    // Impuls-Pin auf low
#if RICHTUNG > 0
  digitalWrite(DIRP, (p < 0) ? HIGH : LOW);   // Richtungs-Pin fuer Stepper
#else
  digitalWrite(DIRP, (p > 0) ? HIGH : LOW);   // Richtungs-Pin fuer Stepper
#endif
  p = abs(p);                                 // 0 .. 512 -> Geschwindigkeit
  if (p > Pmin ) {                             // aus Ruhezone?
    digitalWrite(ENABLE, HIGH);               // Enable 1
    p = constrain(p, Pmin, Pmax);             // Werte begrenzen
    if (digitalRead(SWITCH)) {
      f = map(p, Pmin, Pmax, Fmin, Fmax);     // und Poti -> Frequenz mappen
    } else {
      f = map(p, Pmin, Pmax, Fmin, Fmax / RFAKTOR);
    }
    if (f > 63) {                             // delayMicroseconds kann max. 16383us
      d = 1000000L / f;
      d -= 234;                               // Laufzeitkorrektur um 234us
      if (d > 0) delayMicroseconds(d);
    } else {                                  // bei > 16ms keine Laufzeitkorrektur noetig
      delay(1000 / f);
    }
    digitalWrite(STEP, HIGH);                 // Step an Impuls-Pin
#if RELEASE > 0
  } else {
    digitalWrite(ENABLE, LOW);                // Enable 0 - "loslassen"
#endif
  }
}


}

Danke mal im Voraus und Grüße,
Lukas

In der Tat ist "lcd.clear" eine schlechte Lösung, die man nicht ständig in der loop verwenden sollte.

  1. Du solltest dein LCD immer nur dann beschreiben, wenn sich Werte verändert haben.
  2. Du brachst keine While-Schleifen in deinem Sketch, da du schon eine Schleife (loop) hast. Löse es mit if-Abfragen. While-Schleifen blockieren deinen Sketch zusätzlich.

Danke für die Antwort Dieter!

Hab ich das mit der Wertänderung so richtig verstanden?

Ich lese den Poti noch ein zweites mal ein und speichere es in "pOld" um damit folgende if zu erstellen?

if(pOld!=p)
    {
      lcd.print(f); -> gemapter Potiwert als Ausgabe
    }

Grüße Lukas

Ein 2. Mal einlesen ist falsch.

int pOld = 0;

// im loop:
int p = messen();
if (pOld != p) {
  pOld = p;
  lcd.print(p);
}

Gruß Tommy

Ich lese den Poti noch ein zweites mal ein und speichere es in "pOld" um damit folgende if zu erstellen?

Du speicherst dir doch schon p als globale Variable.
Mal abgesehen davon, dass der Rest der Welt so kurze Namen lieber für lokale Variable mit sehr begrenzter Sichtbarkeit verwendet, mach es doch so:

int p; // Poti-Wert ( -512 ... 511,  geglättet)

void loop() {
   ...
   int pneu= aRead();         // geglaetteten Potiwert holen
   if (p != pneu) {
      p = pneu;
      Serial.print("neuer Potiwert : "); Serial.println(p);  // Wahlweise hier erst mappen usw...
   }
...
}

Normal formatiert seine Ausgaben auf eine konstante Breite. Dann muss man nicht alles löschen. Wenn ein Text dann mal kürzer ist wird der Rest mit Leerzeichen überschrieben

Danke für die Antworten jetzt hat es klick gemacht! :slight_smile:
Werde das am Abend mal testen, wie sich das in Kombination mit dem Schrittmotor auswirkt! :slight_smile:

Grüße,
Lukas

Hallo Luke_Duke,

Schrittmotoren haben es überhaupt nicht gerne wenn die Schritte mit auch nur leichtem zeitlichen Verzug ausgegeben werden. In gewissen Grenzen kann ein Schrittmotor von einem Schritt zum anderen der Schrittfrequenz folgen.
Man kann in gewissen Grenzen beschleunigen und abbremsen. Aber dann ist das ja eine gewollte Geschwindigkeitsänderung. Wenn du während der Schrittausgabe mal LCD ausgaben machst und mal nicht
muss das zum ruckeln des Schrittmotors führen.

Wenn du die Schrittausgabe unbedingt selbst programmieren willst, dann müsstest du das so organisieren:

Es wird eine minimale Zeiteinheit definiert die der höchsten Schrittausgabefrequenz entspricht.
Die loop() benutzt millis() oder micros() und gibt immer dann wenn diese Mindestzeit um ist
einen Schritt aus oder nicht je nachdem welche Schrittfrequenz du ausgeben willst.
Man kann dann als kleinere Schrittfrequenz eben nur vielfache dieser Mindestzeit ausgeben. Alle anderen Dinge die in der loop() gemacht werden dürfen dann höchstens 99% dieser Minimalzeit verbrauchen damit die höchste Schrittfrequenz unter allen Umständen gehalten wird.

Wenn man das alles umgehen will benutzt man einfach die library accellStepper. Die organisiert im Hintergrund eine regelmäßige Schrittausgabe und liefert auch gleich noch beschleunigen und abbremsen mit.

Oder du nimmst einen zweiten Arduino der nichts anderes als die Schrittausgabe macht.

Wenn der Spaß daran besteht alles selbst zu programmieren macht man es selbst. Ich bin mir sicher du wirst eine Menge dabei lernen. Wenn es nur darum geht einen Schrittmotor laufen zu lassen dann benutze accelStepper.
*Wenn es darum geht mit möglichst kleinem (Programmieraufwand 3 Achsen XYZ die von Schrittmotoren angetrieben werden koordiniert anzusteuern benutze GRBL. Das ist eine CNC-Firmware *

*viele Grüße *Stefan

StefanL38:
...Die organisiert im Hintergrund eine regelmäßige Schrittausgabe...

Nein, das tut sie nicht. Sie macht es genau so, wie Du es vorher für's 'selbermachen' beschrieben hast. Aktionen im loop beeinflussen also die Schrittausgabe auch genauso wie vorher beschrieben, und es muss darauf geachtet werden, dass die run() Methode der AccelStepper oft genug aufgerufen. wird. Allein die I2C-Aufrufe der LCD Lib begrenzen damit auch bei der AccelStepper schon die maximale Schrittfrequenz.

Die Erfahrung hab ich leider auch gemacht -> wie ich das Blatt drehe und wende, mit dem LCD wird der Stepper einfach zu langsam :frowning:

Grüße,
Lukas

Du könntest es mit meinem MobaTools versuchen. Die machen das tatsächlich im Hintergrund, (fast) ohne dass sie von den Aktionen im loop beeinflusst werden. Da ist die maximale Schrittfrequenz aber auf 2500 steps/sec festgelegt ( bei den 'standard' Arduinos mit ATmega ). Das funktioiniert dann allerdings auch für bis zu 6 Stepper. Wenn Du nur einen oder 2 hast, kann man das mit Änderung eines defines auch auf bis zu 5000 Steps/Sec bringen.

Also ich habe mal ein bißchen gegoogelt und bin auf diese Seite hier gestoßen:

Da werden die Schritte über einen TimerInterrupt ausgegeben. Auf die schnelle habe ich aber noch nicht verstanden ob man diesem Programm Eine Wegstrecke vorgeben kann dann sagt Schrittausgabe Start, dann wird genau die passende Anzahl Schritte ausgegeben und dann wieder angehalten. Also "Set and forget"-Betrieb.

Käme vielleicht auf einen praktischen Versuch an.

Hier für die Interrupt-Spezialisten der Dmeo-code wie er auch in der Internetseite oben "abgedruckt" ist

/*
 * Drives stepper using a pololu stepper driver and timer interrupts
 * 
 * This example uses pinouts associated with RAMPS 1.4 z-axis
 * 
 * Last edited by Matthew 11/14/2016 Arduino 1.6.7
 *                projectsfromtech.blogspot.com
 * TRCCR1A/B               
 * COM1A = 0b00 - disconnect OCR
 * WGM1 = 0b0100 - Fast PWM with the top value at compare match
 * CS1  = 0b001 - no prescaling               
 * ICNC1 = ICES = 0b0 - doesn't apply
 * 
 * */

const byte Z_STEP_PIN    =     46;
const byte Z_DIR_PIN     =     48;
const byte Z_ENABLE_PIN  =     62;  //62

//Interrupt Variables
volatile uint16_t PulseOnISRNum = 0;
volatile uint16_t isrSincePulse = 0;

//============================================================================
void setup() {
  Serial.begin(115200);

  pinMode(Z_STEP_PIN, OUTPUT);
  pinMode(Z_ENABLE_PIN,OUTPUT);
  pinMode(Z_DIR_PIN, OUTPUT);

  //setup Timer1
  TCCR5A = 0b00000000;
  TCCR5B = 0b00001001;
  TIMSK5 |= 0b00000010;       //set for output compare interrupt
  sei();                      //enables interrups. Use cli() to turn them off
}

float targSpeed = 2.5;       // mm/s
float PPS = 0;               // Pulses Per Second
int8_t DirFlag = 1;          // Direction flag. Set this to keep track of location
int32_t Location = 0;        // nanometers (m*10^-9) scaled by 10^-6 to avoid floating point math in interrupt

long clk = micros();


//============================================================================
void loop() {
  //Set Direction
  digitalWrite(Z_DIR_PIN, LOW);     // Low is forward   (based on setup)
  DirFlag = 1;
//  digitalWrite(Z_DIR_PIN,HIGH);       // High is backward (based on setup)
//  DirFlag = -1;
  digitalWrite(Z_ENABLE_PIN , LOW);   // Active Low

  // Set Speed - these calculation are based on your harware setup
  //           - Mine are for 1/16 microstepping and an m5 threaded rod driving the stage
  //------------------------
  for(float ind = 0 ; ind <3.0 ; ind = ind+0.0005)
  {
  targSpeed = ind;            // mm/s
  PPS = targSpeed * 4000;     //Pulses/s
  OCR5A = 16000000/PPS - 1;   //equation from pg 146 in datasheet- removed factor of 2 b/c I am manually pulsing in an interrupt every time
  Serial.print("Speed (mm/s): ");
  Serial.print(targSpeed);
  Serial.print("  Loop Time (ms): ");
  Serial.print(micros()-clk);
  Serial.print("  Location (mm): ");
  Serial.println(Location/1000000.);
  clk=micros();
  // Input other code here! Stepper driver will run even if this code is blocking!


}}

//================================================================================




ISR(TIMER5_COMPA_vect) {
//    digitalWrite(46, HIGH);       // Driver only looks for rising edge
//    digitalWrite(46, LOW);        //  DigitalWrite executes in 16 us  
    //Generate Rising Edge
    PORTL =  PORTL |= 0b00001000;   //Direct Port manipulation executes in 450 ns  => 16x faster!
    PORTL =  PORTL &= 0b11110111;
    Location = Location + 250 * DirFlag ;  //Updates Location (based on 4000 Pulses/mm)
}

da geht es richtig ans eingemachte mit direkter Portmanipulation usw.
viele Grüße Stefan

So jetzt habe ich doch noch eine Frage:
Da hattest am ANfang geschrieben Vorschub für eine Fräse. Verstehe ich das richtig die Fräse ist konventionell bzw. bisher alles Handbetrieb und jetzt soll speziell der Vorschub durch einen fetten Schrittmotor auf konstanter Geschwindigkeit fahren?

Das würde bedeuteten du bringst den Fräser manuell auf die Position in der er dann loslegen soll sagst "Vorschubstart"
Fräser fräst über das Werkstück und ein Stückchen darüber hinaus um dann wieder manuell gestoppt zu werden.
Dafür würde sogar ein simpler NE555-Schaltkreis reichen. Also wenn es nicht darauf ankommt ob der Fräser 3-5mm früher oder später zum stehen kommt dann könnte man dafür die "Tonausgabe"-Befehle benutzen. Die benutzen auch einen Timerinterrupt um ein Rechtecksignal zu erzeugen.

Wenn der Fräser auf den hundertstel Millimeter genau anhalten soll dann würde ich in der Anwendung in der Tat zu einem Extra-Arduino raten der nur die Schrittausgabe macht.

Oder wenn du gleich die ganze Fräse auf CNC-umrüsten wolltest zu ESTLCAM raten. ESTLCAM ist eine Software die sowohl drei Schrittmotorachsen gleichzeitig für echtes 3D-Fräsen ansteuern kann als auch eine Software mit der man mit ein paar Mausklicks aus 2D-CAD-Zeichnungen das Fräsprogramm erstellen kann. Kosten schlappe 50 Euro.

So jetzt müsstest du mal sagen was du für Anforderungen an die Schrittmotorsteuerung hast. +-1 bis 2 Sekunden Abschalttoleranz oder auf 0,01 mm genaues Starten / Stoppen?

viele Grüße Stefan ( Der selbst eine CNC-Fräse hat )

So jetzt habe ich zuerst für den ESP32 eine ISR für Schrittausgabe programmiert und getestet und dann für Arduino UNO.

Auf dem ESP32 bekommt man bis zu 50 KHz als Schrittfrequenz hin. (Wird ja auch mit 80 MHz getaktet.
Auf dem Arduino sind es 20 kHz.

Die Schrittausgabe funktioniert set and forget.
Man setzt die Variable StepCounter auf die Anzahl Schritte die ausgegeben werden soll
dann setzt man das Flag CreateStepSignal = true
Dann werden exakt die Anzahl Schritte ausgegeben die man angegeben hat und dann wird die Schrittausgabe gestoppt.

Beim Arduino habe ich aber noch nicht herausgefunden wie man gezielt die Schrittfrequenzen setzt.
Da blicke ich mit dem prescaler und dem compare register noch nicht richtig durch.
Klar kann ich die Wert unterschiedlich setzen aber wie berechnet sich die Frequenz mit der dann der Interrupt aufgerufen wird? Ich habe mir mehrere Seiten durchgelesen die das erklären wollen aber nicht wirklich gebacken kriegen es allgemein zu erklären. Es steht zwar die eine Frequenz erklärt aber nicht wie man das für beliebige Frequenzen macht.
Außerdem scheint es eine zusätzliche Grenze zu geben, dass das ausführen des ISR-codes natürlich auch seine Zeit braucht und damit die maximal mögliche Frequenz weiter unten liegt als die Frequenz mit der der Counter-Match
auftritt.

so hier also die Codes
Einmal für ESP32

// ESP32-Democode for a periodic TimerInterrupt 
// used for set-and-forget steppermotor-output

//nbt nonblockingtimer 
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}
unsigned long BlinkTimer = 0;
const int     OnBoardLED = 2;

unsigned long TestTimer = 0;

volatile int HighCounter = 0;
volatile int StepCounter = 0;
volatile boolean CreateStepSignal = false;
volatile boolean PinHIGH = false;

unsigned long StepFreqHz = 50000;
unsigned long AlarmCount = 20000000 / StepFreqHz;
const int signalPIN = 4;

 
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
 
void IRAM_ATTR onTimer() {
  portENTER_CRITICAL_ISR(&timerMux);
  if (CreateStepSignal) 
  {    
    if (StepCounter > 0) 
    { 
      if (PinHIGH) 
      {
        digitalWrite(signalPIN,HIGH);    
        PinHIGH = false;
      }
      else 
      {
        digitalWrite(signalPIN,LOW);
        PinHIGH = true;
        StepCounter--;
      }
    }
  }  
  else // StepCounter was counted down to 0
  {CreateStepSignal = false;}  
  portEXIT_CRITICAL_ISR(&timerMux); 
}
 
void setup() {
  Serial.begin(115200);
  pinMode(OnBoardLED, OUTPUT);
  pinMode(signalPIN, OUTPUT);
  digitalWrite(signalPIN,LOW);
  
  timer = timerBegin(0, 1, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, AlarmCount, true);
  //timerAlarmEnable(timer); 
  
}
 
void loop() {

  if ( TimePeriodIsOver(BlinkTimer,500) ) {
    digitalWrite (OnBoardLED, !digitalRead(OnBoardLED) ); // Blink OnBoard-LED
    Serial.print(StepFreqHz);    
    Serial.print(" ");    
    Serial.print(AlarmCount);    
    Serial.print(" ");    
    Serial.println(StepCounter);    
  }
  
  if ( TimePeriodIsOver(TestTimer,8000) ) {
    Serial.println("testing set and forget step-output");
    if (StepCounter <= 0) {
      if (StepFreqHz == 50000) { StepFreqHz = 25000; } else { StepFreqHz = 50000; }
      // ESP32-CPU-freq is 80 MHz 
      // 4x calling the ISR creates one LOW/HIGH-pulse = 20 MHz
      AlarmCount = 20000000 / StepFreqHz; 
      timerAlarmDisable(timer); 
      timerAlarmWrite(timer, AlarmCount, true);
      StepCounter = 100000;
      CreateStepSignal = true;
      timerAlarmEnable(timer);       
    }
  }
}

einmal für Arduino Uno

//uses timer2 

//nbt nonblockingtimer using millis()
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}

const int signalPin = 9;

unsigned long BlinkTimer = 0;
const int     OnBoardLED = 13;

unsigned long DebugTimer = 0;

volatile long  StepCounter = 0;
volatile boolean CreateStepSignal = false;
volatile boolean PinHIGH = false;


void setup(){
  
  //set pins as outputs
  pinMode(signalPin, OUTPUT);
  pinMode(OnBoardLED, OUTPUT);
  digitalWrite(signalPin,LOW);
  digitalWrite(OnBoardLED,LOW);
  Serial.begin(115200);

  cli();//stop interrupts

//setup timer2 
  TCCR2A = 0;// set entire TCCR2A register to 0
  TCCR2B = 0;// same for TCCR2B
  TCNT2  = 0;// initialize counter value to 0
  
  OCR2A = 4;
  // turn on CTC mode
  TCCR2A |= (1 << WGM21);
  // Set CS2X bit for prescaler
  TCCR2B |= (1 << CS22);   
  // enable timer compare interrupt
  TIMSK2 |= (1 << OCIE2A);

sei();//allow interrupts

}//end setup

  
ISR(TIMER2_COMPA_vect){

  if (CreateStepSignal) 
  {    
    if (StepCounter > 0) 
    { 
      if (PinHIGH) 
      {
        digitalWrite(signalPin,HIGH);    
        PinHIGH = false;
      }
      else 
      {
        digitalWrite(signalPin,LOW);
        PinHIGH = true;
        StepCounter--;
      }
    }
    else // StepCounter was counted down to 0
      {CreateStepSignal = false;}  
  }  
}


void loop(){
  if ( TimePeriodIsOver(DebugTimer,500) ) {
    digitalWrite (OnBoardLED, !digitalRead(OnBoardLED) ); // Blink OnBoard-LED
    Serial.println(StepCounter);    
  }
  
  if ( TimePeriodIsOver(BlinkTimer,8000) ) {
    Serial.println("testing set and forget step-output");

    if (StepCounter <= 0) {
      if  (OCR2A == 4) { OCR2A = 8;} else {OCR2A = 4;}
      StepCounter = 100000;
      CreateStepSignal = true;
    }
  }
}

@FRanz-Peter: wie machst du die Schrittausgabe in den MobaTools?

viele Grüße Stefan

Hallo Stefan,
bei den MobaTools läuft das auch über einen Timer-Interrupt. Der ist aber rel. komplex und muss einiges berechnen, da ja auch Anfahr- und Bremsrampe unterstützt werden. Und Du kannst auch während der Bewegung jederzeit alle Parameter ändern: Geschwindigkeit, Rampe oder ein neues Ziel vorgeben. Ist das dann z.B. so nah, dass es mit der eingestellten Bremsrampe nicht erreicht werden kann, fährt er übers Ziel hinaus, und dann mit entsprechender Anfahr- und Bremsrampe wieder zurück zu dem neu vorgegebenen Zielpunkt. Durch die ganzen Berechnungen ( geht ja auch parallel für mehrere Stepper ) ist deshalb die maximale Schrittfrequenz deutlich kleiner. Auf einem ESP8266 kommt man aber auch für mehrere Stepper parallel immerhin auf ca 8000 Steps/sec. Den ESP32 unterstützen die MobaTools (noch?) nicht.