ESP32: Step Pulse Experiments with Timers

(Edit: Also check out my better “RMT” way to send the pulse)

I have been playing with the ESP32 microcontroller to see how well it would perform as a small scale CNC controller. The low cost and high performance as well as the built in Wifi and Bluetooth make it very attractive.

One of the challenges is step pulse timing. Most stepper drivers work with a direction signal and step signal. The step signals are a short pulse for each step. If they are too short, the driver will not detect them. If they are too long, it limits the rate at which you can send them.

You first set the direction signal high or low depending on the direction you want the motors to spin. You then send the step pulse. The direction signal has to be stable for a short period of time before the step signal is sent. The process is…

  1. Set direction
  2. Wait a bit (if it changed)
  3. Turn on the step pulse signal.
  4. Wait a bit
  5. Turn off the step pulse signal.

The timing is critical and varies by motor driver. Here is a typical spec.

Here are the specs for a few of the stepper drivers I regularly use.

 Allegro A4988TI DRV8825Toshiba TB6600
Direction Delay200ns650ns?
Step Pulse Delay1us1.9us2.2us

Test Firmware

Typically the firmware motion planner determines when to take a step, then sets an interrupt to occur at that time in the future. This allows the firmware to do other things like interacting with the user and planning future moves while it is waiting for the interrupt.

To simulate a stream of pulses, I created a timer interrupt that would case steps to occur at a constant 5kHz rate. That is onStepperDriverTimer() in the code.

In that interrupt service routine I first set the direction pin. Normally you only need to set it when the direction changes, but it will be easier to see this on a logic analyzer if I change it every time for this test. I then need to wait a little time before setting the step pulse pin. I could use another interrupt to do this, but the time is so short at about 750ns, that it is better to just waste a few cycles. In the CNC software I will only need to do this when the direction changes. That will be at the few beginning of the acceleration when the step rate is the slowest. I do this delay with a few NOP()s. The are “no operations”.

I then setup the the interrupt to end the pulse. That is onStepPulseOffTimer() in the code. I set the step pin after this because those instructions take clock cycles too. I can use those as part of my delay.

When that interrupt occurs, I turn off the step pulse signal. I also turn off the direction in this example. I am only doing it here so I can see that change on the logic analyzer. Normal CNC frmware would just leave it alone because there are typically thousands of steps before the direction is likely to change.

I wrote a program to simulate some CNC firmware so I could play with step pulse timing.

// create the hardware timers */
hw_timer_t * stepperDriverTimer = NULL;  // The main stepper driver timer
hw_timer_t * stepPulseOffTimer = NULL;  // This turns the step pulse off after xx uSeconds

// define the gpio pins
#define STEP_PIN 17
#define DIR_PIN 16

// the step pulse interrupt service routine. 
void IRAM_ATTR onStepperDriverTimer()
{
  // if ... the direction changed from last time (not in this demo)
  digitalWrite(DIR_PIN, HIGH);  // in actual CNC firmware this will go high or low
  for(uint8_t i=0; i<10; i++)
  {
    NOP();  // do nothing for one cycle
  }
  // end if

  // setup the pulse off timer
  timerWrite(stepPulseOffTimer, 0);
  timerAlarmWrite(stepPulseOffTimer, 22, false);  // the alarm point is found by looking at logic analyzer
  timerAlarmEnable(stepPulseOffTimer);  
  
  digitalWrite(STEP_PIN, HIGH); // put it after the timer setup to include the timeto do that
}


// 
void IRAM_ATTR onStepPulseOffTimer()
{
  digitalWrite(STEP_PIN, LOW); // end step pulse 
  digitalWrite(DIR_PIN, LOW); // only here for dem program CNC firmware would leave this until direction change
}


void setup() {  

 pinMode(DIR_PIN, OUTPUT);
 pinMode(STEP_PIN, OUTPUT);

    
 stepperDriverTimer = timerBegin(0, 4, true); // 80Mhz / 4  = 20Mhz// setup stepper timer interrupt ... this will simulate a flow of steps
 stepPulseOffTimer = timerBegin(1, 1, true); // 

 // attach the interrupts
 timerAttachInterrupt(stepperDriverTimer, &onStepperDriverTimer, true);// attach the interrupttimerAttachInterrupt(directionDelayTimer, &onDirectionDelayTimer, true);// attach the interrupt
 timerAttachInterrupt(stepPulseOffTimer, &onStepPulseOffTimer, true);// attach the interrupt
 
// setup the time for the 
 timerAlarmWrite(stepperDriverTimer, 4000, true);  // 20Mhz / 4000 = 5kHz rate ... this is the only one that auto repeats  
 timerAlarmEnable(stepperDriverTimer); 
 
}

void loop() {
  // no loop code required.:
  
}

Results

Here is a picture of my setup.

This is screen shot of what the logic analyzer captured. The upper line is the step signal and the lower line is the direction signal. The direction signal comes on first and then the step pulse signal comes on 0.75us later. The step pulse then lasts for about 2.5us before turning off.

Next Steps

  • I’ll go forward this method to see how well it works in actual CNC firmware.
  • I have been programming in the Arduino-ESP32 environment. This is an easy way to learn about the peripherals and do some quick tests. I may switch to the ESP-IDF¬† in the future.
  • I would like to investigate the RMT features of the ESP32. It is designed for Remote Controls, but I have heard it is quite flexible and might help with pulse generation.

 


If you want to be notified of future blog posts, please subscribe.

Share and Enjoy:
  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  1. No Comments

*