Keep a stable and predictable step-motor's rotation speed

Hello everyone, I'm new on this forum and I need your help to optimize my Arduino code:
I'm building a "barn door astrophotography tracker" which allows me to perform long exposures to the night sky by avoiding the star-trail effect on the image.
The rotation is done with a NEMA-17 step motor driven by a TB6600 driver, connected to an Arduino UNO R3 board, and here below you can see the code I'm using for it:

cconst int PUL = 7; // Pulse pin
const int DIR = 6; // Direction pin
const int ENB = 5; // Enable Pin

const float STP = 6400; // Motor Steps per Revolution
const float MULT = 3; // Gears multiplier 

float HalfStepDelay = 1;


void setup() {
  pinMode (PUL, OUTPUT);
  pinMode (DIR, OUTPUT);
  pinMode (ENB, OUTPUT);
  digitalWrite(ENB, HIGH);
  digitalWrite(DIR, LOW); // Forward Direction

  float StepDelay = 60000000 / (STP * MULT); //  3125
  HalfStepDelay = (int)(StepDelay / 2);      //  1562

}  

void loop() {
  digitalWrite(PUL, LOW);
  delayMicroseconds(HalfStepDelay);
  digitalWrite(PUL, HIGH);
  delayMicroseconds(HalfStepDelay - 35 ); 
}

I want to have the final gear to make 1 turn per minute, so (as the gear reduction is 1:3), the motor should do 3 turn per minute.

Everything works fine (but suggestions to optimize it are always welcome), but you'll notice that I had to remove those "35" microseconds to the last command in order to have the correct rotation speed (I got this value just by empirically testing various values) because, I think, those 35us are the ones "stolen" by the execution of the program itself.
So this means that if I add more code lines (i.e.: let's say I want to add some input button check, or maybe an output display to show the current speed, or stuff like that) the code will take more time to run and the final speed of the motor will be impacted (and '35' will need a different value).
This causes the 'HalfStepDelay' value to become hard to calculate, so I'm looking a better solution to keep my rotation speed stable, predictable, and not impacted by the code itself.
For example:

  • Implement something different than Delay to separate the motor's steps
  • Make the Delay to rely on system's clock (i.e.: change the motor's state if 'x' clock tiks have been elapsed since last step)
  • anything else...?

Any help or suggestion will be very welcome.

Thank you
A.

Have a look at the singleStep() function in the second example in this Simple Stepper Code

By using millis() rather than delay() the time taken by the rest of the Arduino code is automatically allowed for.

If more precise timing is needed you can use micros() in place of millis().

...R

The only way you will achieve a truly consistent, accurate step rate, limited only by the accuracy of your Arduinos CPU clock frequency, is to use a hardware timer to issue the step pulses. Any other approach, and you will be at the mercy of interrupts, which will delay some of your pulses. Using a timer, they WILL occur at the precise interval you program the timer for.

And, many Arduinos use ceramic resonators, rather than precision quartz crystals, for their clocks, which can both have significant frequency error, and the frequency can change significantly with temperature. So, for best performance, you should use an Arduino with a crystal, not a resonator.

Using a hardware-timer rises another question. You can't sweep through all frequencies with a hardware-timer
because the timer has a pre-scaler.

3 turns in one minute in halfstep-mode means:

3*400 = 1200 steps per minute
1200/ 60 = 20 steps per second.

Assuming you need exact 20 steps per second: can the hardware-timer be setup to create exact 20 pulses per second?

This all is about longterm-stability. A simple step that is gliched for some microseconds doesn't play a role.
So using micros() might be a solution too because the waiting-time is 25000 microseconds

A chrystal will be sure more precise than a ceramic resonator.
How about using a microcontroller with a higher cpu-frequency?

best regards Stefan

StefanL38:
Using a hardware-timer rises another question. You can't sweep through all frequencies with a hardware-timer
because the timer has a pre-scaler.

3 turns in one minute in halfstep-mode means:

3*400 = 1200 steps per minute
1200/ 60 = 20 steps per second.

Assuming you need exact 20 steps per second: can the hardware-timer be setup to create exact 20 pulses per second?

This all is about longterm-stability. A simple step that is gliched for some microseconds doesn't play a role.
So using micros() might be a solution too because the waiting-time is 25000 microseconds

A chrystal will be sure more precise than a ceramic resonator.
How about using a microcontroller with a higher cpu-frequency?

best regards Stefan

First, that is not an issue at all for something that is running at constant speed. Second, it is not a limitation even if you are doing acceleration ramps. You can use a single, fixed divider, and skip the output of some steps to stretch the time between actual step outputs. If you need a step rate that is twice the full duration of the counter, you only actually output a step pulse every other time the counter rolls over. You can cover the entire range of speeds from barely a crawl to the full speed the motor can handle with a single well-chosen divider setting. At the slower speeds you just might be only outputting a step pulse every 16, 32 or more counter cycles. This is one the many reasons the timers can be set to NOT change the output pin on rollover or compare match. You can also change the counter compare value on every cycle, by enabling the compare interrupt. That is how you generate acceleration and deceleration ramps where the step time changes on every single step.

The pre-scalers are just to divide the source clock. You can still play with the "TOP" value to generate the PWM/pulse signal.

i.e.

For a 16 MHz clock with different pre-scalers, you can generate any frequency that satisfies the following equation

PWM = (16 000 000/prescaler)/(TOP + 1)

where TOP is from 1 to 0xFFFF

20 pulses/sec would be pre-scaled with 16 and TOP of 49 999

((16 000 000 / 16)/(20)) - 1

With the TOP and available pre-scalers, there's plenty of wiggle room to generate any frequency up to 8 MHz

hzrnbgy:
With the TOP and available pre-scalers, there's plenty of wiggle room to generate any frequency up to 8 MHz

Cool! How do I get 6.4 MHz? :slight_smile:

Well, you can always put a 12.8 MHz or 19.2 MHz crystal in lieu of the 16MHz one

I get the distinct impression from the Original Post that the "kludge" adjustment by 35 µsecs give the OP sufficient accuracy and stability.

If I am correct then I don't see any need for any of the complications being discussed - such as the use of a Hardware Timer.

...R

This tutorial Multi-tasking in Arduino covers driving a stepper motor at a constant speed using the AccelStepper library.
That tutorial includes details on how to measure the time the rest of your code is taking and how to 'compensate' for that.
Basically for 'stable' speed you need to call the AccelStepper.run() method often and then just let it do its work.
The tutorial covers using the loopTimer class (available in my SafeString library, from the library manager) to measure the variation the stepper speed.

Really thanks to everyone for your precious help: a lot of good ideas and things to try.
As you already understood, even if I'm using the 35 µsecs adjustment, my "real" target is to feel free to add more lines of code (let's say I want to add buttons to increase/decrease the speed and a display for speed monitoring) without impacting the "real" speed of the motor itself caused by other commands execution time.
I'll do some test following your suggestions, and for sure I'll follow-up in this thread soon with my findings, so thanks again everyone!

acavallari69:
my "real" target is to feel free to add more lines of code

What I suggested in Reply #1 should deal with that - provided the extra code does not use delay() or a long running WHILE or FOR loop

...R

Robin, actually the solution that I'm testing now is the one suggested by you, by using micros() for motor's step triggering, so I'll be able to give a feedback about it quite soon :wink:
Thank you
A.

I fail to understand the resistance to simply using a hardware timer to issue the step pulses. It gives you dead accurate, consistent, jitter-free step pulses, and leaves your "foreground" code fully available to do whatever other work you please, even using delay(), Serial, etc., without having to worry at all about screwing up the step timing. The timer code is not at all complex. Being able to start a move, then go away and have it carry on totally in the background makes the whole application much simpler, and you'll never have to worry about a mis-timed step causing the motor to stall. And, if it matters (perhaps not in this application, since the motor is presumably turning rather slowly), it is easy to add any desired acceleration and deceleration curves.

RayLivingston:
I fail to understand the resistance to simply using a hardware timer to issue the step pulses.

It may involve programming knowledge that is currently beyond the OP's grasp.

I wouldn't go to the trouble of using a HardwareTimer if I could achieve the result I needed with micros() or millis()

...R

Right: No problem on using the timer HW (really I just ordered one a couple of days ago, which may help with this or maybe future projects) but I wanted to explore the possible solutions with the hardware I have right now, so I'm not excluding it at all, I'm just saving it for last.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.