ESP32 stepper-motor step-signal-creation with periodic timer-interrupt

Hi everybody,

I have searched for code that can create high-pulses at high (=100 kHz) and very constant frequency to drive a stepper-motor with STEP/DIR-inputs.
I have found this demo-code who does start the timer and the timer runs infinetly.

// ESP32-Democode for a periodic TimerInterrupt 
volatile int interruptCounter;
int totalInterruptCounter;
 
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
 
void IRAM_ATTR onTimer() {
  portENTER_CRITICAL_ISR(&timerMux);
  interruptCounter++;
  portEXIT_CRITICAL_ISR(&timerMux);
 
}
 
void setup() {
 
  Serial.begin(115200);
 
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, 1000000, true);
  timerAlarmEnable(timer);
 
}
 
void loop() {
 
  if (interruptCounter > 0) {
 
    portENTER_CRITICAL(&timerMux);
    interruptCounter--;
    portEXIT_CRITICAL(&timerMux);
 
    totalInterruptCounter++;
 
    Serial.print("An interrupt as occurred. Total number: ");
    Serial.println(totalInterruptCounter);
 
  }
}

Me target is to have code that does this high-frequency high-pulses but can be stopped on one-step-accuracy

This means I setup a command "run stepper-motor for 200.001 steps" and the code is outputting exactly 200.001 steps and then stops the signal-train of high-pulses.

I guess at high frequencies like 200 kHz stopping the timer by software through if-conditions will take too much time to be one-step-accurate. So my basic idea is to use a second hardware-counter which is setup to the number of steps (200.001) and counts down each time the timer-interrupt occurs. If this second hardware-counter reaches zero this second hardware-counter immediately stops the timer-driven interrupt or the output-pin.
Can two hardware-timers of the ESP32 be configured in that way?

Do you have another idea how this functionality of a "set-and-forget"-rock-solid-constant-timing-step-accurate"-pulse-train-generator can be coded?

best regards Stefan

OK so I just tested the maximum possible frequency when using the code above and add step-output limitation
50 kHz is possible

here is a demo-code that demonstrates the set-and-forget step-output

You setup variable StepCounter to the amount of steps the stepper-motor should run
then set flag CreateStepSignal = true

then the exact number of steps is created by interrupt completely independent of what the main-code does

// 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);       
    }
  }
}

best regards Stefan