Stepper motor with millis/micros

I am trying to get a simple non-blocking replacement for:

digitalWrite(stepPinX, HIGH);
delayMicroseconds(500);
digitalWrite(stepPinX, LOW);
delayMicroseconds(500);

This is what I tried, and the stepper motor stays still:

const int dirPinX = 0;
const int stepPinX = 2;

unsigned long startMicros = micros();
unsigned long currentMicros;
const unsigned long period = 500;

void setup()
{
  pinMode(stepPinX, OUTPUT);
  pinMode(dirPinX, OUTPUT);

  digitalWrite(dirPinX, HIGH);
}

void loop()
{
  stepX();
}

void stepX()
{
  currentMicros = micros();  //get the current "time" (time since program start)

  if (currentMicros - startMicros >= period)  //test whether period elapsed
  {
    digitalWrite(stepPinX, HIGH);
    startMicros = currentMicros;
  }
  if (currentMicros - startMicros + period >= period)
  {
    digitalWrite(stepPinX, LOW);
    startMicros = currentMicros;
  }  
}

Any suggestions on how to correct this? (I do not want to use a library like Accelstepper. I want to use millis/micros.)

Step through the code logically.

currentMicros - startMicros >= period will always be true before currentMicros - startMicros + period >= period is true. Then the second if will immediately be true because startMicros will be equal to currentMicros and period will be >= period. There will not be any delay between the stepPinX pulse.

That is why the motor never moves.

i think you want something like this
fix the pins (i use LEDs). doubt you can use pin 0, the TX pin
cnt is the # of toggles, not steps

const int dirPinX  = 13;
const int stepPinX = 12;

unsigned long startMicros;
unsigned long currentMicros;
const unsigned long period = 500;

int cnt;

void setup()
{
    pinMode(stepPinX, OUTPUT);
    pinMode(dirPinX, OUTPUT);

    digitalWrite(dirPinX, HIGH);

    cnt = 2*50;
}


void loop()
{
    stepX();
}


void stepX()
{
    if (0 >= cnt)
        return;

    currentMicros = micros();  //get the current "time" (time since program start)
    if (currentMicros - startMicros >= period)  //test whether period elapsed
    {
        startMicros = currentMicros;
        digitalWrite(stepPinX, ! digitalRead (stepPinX));
        Serial.println (cnt);
    }
}

It looks like "cnt" does nothing. Did you forget to include something?

The baud rate isn't declared. Not sure why cnt = 100.

Yes, there needs to be a Serial.begin() in setup, use a very high baud rate if you are going to print within the right tight timing which is the step mechanism.

a7

This application has nothing to do with printing to the serial monitor. I was just pointing out there seems to be things missing from whatever gcjr was saying.

did you check the pins #s?

It could be because it is assigned the value of 2*50 in the setup() function.

And that code prints, but has no Serial.begin(), so put one in or take out the printing.

And try

 cnt--;

somewhere in there, maybe after you do or don't print.

a7

The pin numbers for the stepper motor? Yes, they are correct. Works normally with the original:

digitalWrite(stepPinX, HIGH);
delayMicroseconds(500);
digitalWrite(stepPinX, LOW);
delayMicroseconds(500);

Doesn't look like it would.

This

   startMicros = currentMicros;

should not be done every time X is called. It should be done at the end of the timing period so as to begin the new timing period.

If we are in the first period, set the output HIGH.

If we are in the second period, set the output LOW.

When we pass the end of the second period, reset startMicros().

Or get @gcjr's code to work. It looks like it shoukd, at a glance.

It uses the time period to do one thing occasionally - toggle the output line.

As such it can be coded in the idiomatic manner, no split cycle stuff what is not working for you, yet, and is frankly somewhat confusing to verify without actually running it. For me.

a7

@gcjr is on the right track…
That code can be simplified by simply toggling the step pin on every tick,
This creates a step rate of (period x 2).

You can rework that too, to be even simpler.

Untested. Under the umbrella here.

a7

gcjr code doesn't make any sense.

    cnt = 2*50;

then

    if (0 >= cnt)
        return;

That's it. This functionally does nothing.

Did you see post #12?

Because I already changed startMicros = currentMicros; to the end.

Well if you just want a non-blocking way of creating step-pulses
use the mobatools library.

This library uses a timer-interrupt to create the step-pulses "in the backround"
You can execute a command like

myStepper.doSteps(10000);

And then go on with executing some other code while in "parallel" the step-pulses are created.

Set and forget.

It is different if you want to learn non-blocking timing yourself which of course will be useful in many cases

best regards Stefan

Did you see post #13, where I said that means it runs every time X is called?

Which is wrong.

And which might be fixed by the code I showed in post #15?

Work with us here. Put your finger on the code and see it doing what you wrote, not what you meant.

a7

Yes, there is no need to divide the step interval into two periods to write the step pin high and low.

When the desired interval has passed just pulse the step pin.

digitalWrite(stepPinX, HIGH);
digitalWrite(stepPinX, LOW);

"I do not want to use a library like Accelstepper. I want to use millis/micros."

It is one thing, one timer, one output, one stepper motor.

These two things:

if (currentMicros - startMicros >= period)

if (currentMicros - startMicros + period >= period)

should be one thing. You have to add a variable to keep track if the pulse must go high or low.

What is the minimum pulse length ? Can you do this :

  // make a step
  digitalWrite(stepPinX, HIGH);
  digitalWrite(stepPinX, LOW);

[UPDATE] cattledog wrote in post #20 that a small pulse is good enough.

Why do you use pin 0 for the direction ? Pin 0 is for the Serial communication on a Arduino Uno. Have you told which Arduino board you use ?

This is what a millis timer looks like, I replaced millis with micros and use pin 2 and 3:

// Forum: https://forum.arduino.cc/t/stepper-motor-with-millis-micros/1099939
// This Wokwi project: https://wokwi.com/projects/358757303490537473

const int dirPinX = 3;
const int stepPinX = 2;

unsigned long previousMicros;
unsigned long currentMicros;
const unsigned long period = 500; // 500 microseconds
int pulseState = LOW;             // default low

void setup()
{
  pinMode(stepPinX, OUTPUT);
  pinMode(dirPinX, OUTPUT);

  digitalWrite(dirPinX, HIGH);
}

void loop()
{
  stepX();
}

void stepX()
{
  currentMicros = micros();  //get the current "time" (time since program start)

  // A millis-timer that toggles the output
  if (currentMicros - previousMicros >= period)  //test whether period elapsed
  {
    previousMicros = currentMicros;

    // toggle the pulseState
    if( pulseState == LOW)
      pulseState = HIGH;
    else
      pulseState = LOW;

    digitalWrite(stepPinX, pulseState);
  }  
}

Try the sketch in Wokwi:

Post the code where you took any of my advice into account.

That did not work.

a7