Faster Step Speed

I'm working with the MEGA2560 and am doing early testing of micro-stepper control but my max speed is not fast enough. The motor is a 200 step/rev (1.8 degree) motor being driven by a micro-stepper driver with a resolution of 100 micro-steps/step and the motor then drives a load through a 50:1 harmonic reducer so it takes 1,000,000 steps for one revolution of the load.

Using the digitalWrite function and with pretty much everything else striped away to maximize step rate I'm getting a step speed of just over 80K steps/sec so it takes about 12.5 seconds to complete one revolution of the load. I'd like to increase the speed to 2 seconds per load rev so that would need a step rate from the Arduino of about 500,000 steps per second. I know there are other ways to control the digital pins and that there are faster ways but would appreciate any advise on the best way to get the fastest speed. I seem to remember a way to write to the port directly, but I can't remember where I saw that.

As a further possibility I also have a Teensy 3.6 which I believe to be faster, but for this phase of my testing I'd like to focus on the MEGA2560.

Thanks in advance,

Brian

You need to post the code you are using so we can see what you can see.

...R Stepper Motor Basics Simple Stepper Code

Direct port manipulation is much faster than digitalWrite, but generating pulses at such high rates is not easy on a slow microcontroller.

I'd start by reducing the micro-stepping. When you're travelling fast between positions, you should take big steps. If you need that micro-positioning accuracy, then switch to micro-stepping just before reaching the goal. That makes your system nearly 100x faster already.

MarkT: Direct port manipulation is much faster than digitalWrite, but generating pulses at such high rates is not easy on a slow microcontroller.

...which is exactly why there's dedicated hardware on the Arduino chips which is specifically dedicated to just producing pulses at any speed up to and including the chip's clock speed. On a MEGA, that's 16MHz. In the 80kHz range, there should be no problem other than simply keeping count of the steps.

I found what I was looking for...

The typical digitalWrite function limits me to about 80K step/sec whereas using the PORT write function is much faster. The code snippet for its use is:

PORTE = PORTE ^ B00010000;

This statement using XOR to change bit 4 of port E and the same statement can be used to either set it high or low. You could also use the port function without XOR to set it explicitly high and then explicitly low whereas the XOR method simply toggles the bit. In essence, the 'B00010000' part is the mask for the XOR so that every bit with a '0' will not be changed while every bit with a '1' will be changed. So, in this example only bit 4 of port E changes and all other bits in port E remain unchanged.

This approach gets me to about 440KHz step frequency BUT then you have to deal with the fact that you can't go from zero speed to 440K steps/second instantly as no motor can accelerate that fast so the motor just kind of buzzes. So, now I'm looking into accel/decel code to handle speed ramping.

One limitation with the MEGA2560 is that code execution is kind of slow so that a code sample for Loop of:

void Loop () { PORTE = PORTE ^ B0001000; PORTE = PORTE ^ B0001000; }

Because of the slow code execution speed the duty cycle is no where near 50% -- in fact I measure less than 9% with the ON pulse lasting only 200nS while the OFF pulse is 2.1uS. For whatever reason the code is delayed almost 2uS to loop back. I hope the faster controllers like the Teensy 3.6 or ESP32 are better than that. Additionally, because of the need to using velocity ramping the additional code will further slow down the execution making the need for faster controllers all the more necessary.

I'll repeat these test on my Teensy 3.6 and report what differences I find so long as that's not against forum rules.

Brian

Yes, good to test. For this kind of timing, you can insert null operations NOP or No-Op to pad out the time to get what you want. This has to be done with inline assembly, as there's no C function for NOP. But then your program is totally dependent on the speed of a particular processor. You can't just move the code to a Teensy and expect it to still work.

You should go back to the datasheet and read the section on timers. It's extremely complicated but the hardware designers knew you were going to do this when they designed the chip, so they put in special abilities just for you. Not using those abilities is like buying a schoolbus and complaining that only one person fits in the driver's seat.

Got a link to your stepper driver? It will have a maximum stepping rate. Some have a 200,000 KHz.

justone: Got a link to your stepper driver? It will have a maximum stepping rate. Some have a 200,000 KHz.

Sure, this is from the manual...

4.7.4 Timing Requirements PULSE Max. Frequency .....500 kHz Max. Rise And Fall Times...............1 microsecond Min. Pulse Width....1 microsecond Other Signals Response Time ...... 50 microseconds

So, the max step rate is 500KHz which is where I wanted to be, but my testing using the fastest code possible kept me below that but the hugely asymmetrical duty cycle is a problem and if the ON pulse needs to be 1uS I'd need to pad it with 800nS which would lower the step frequency to a bit over 300KHz.

I should mention this is an old driver, like 20 years old, and is a Superior Electric SLO-SYN SS2000MD7 with max resolution of 100 micro-steps/step. The motors are also 20 years old and from Superior Electric with model numbers: M091-FD-8101 (NMEA34) and M061-LE08 (NMEA23).

Brian

I found a way to pad the ON pulse to be a minimum of 1uS -- here's a code segment to explain.

This is the code that produced the fastest step rate but the ON pulse is too short at 200nS.

void Loop () { PORTE = PORTE ^ B0001000; PORTE = PORTE ^ B0001000; }

This is the modified code that pads the ON time to 1uS.

void Loop () { PORTE = PORTE ^ B0001000;

PORTE = PORTE ^ B0000000; PORTE = PORTE ^ B0000000; PORTE = PORTE ^ B0000000; PORTE = PORTE ^ B0000000; PORTE = PORTE ^ B0000000; PORTE = PORTE ^ B0000000; PORTE = PORTE ^ B0000000;

PORTE = PORTE ^ B0001000; }

As you can see I added 7 lines of port writing using XOR with a mask of B00000000 which does not change the port value at all but does eat some cycles. Using this code the ON time is now just over 1uS and the frequency is a bit over 300KHz.

Brian

Raptor1956: This approach gets me to about 440KHz step frequency ....

void Loop () {
 PORTE = PORTE ^ B0001000;
 PORTE = PORTE ^ B0001000;
}

Read in the datasheet about "Toggling the Pin". You can toggle a pin wit a single instruction.

However if you need the above type of code to get the frequency you need you are operating right at the edge of the capability of the chip and I doubt if you will have the spare capacity to include acceleration - even if you use a HardwareTimer to generate the pulses.

Stepper motor speed control depends on the interval between pulses and you must have CPU time to change that interval.

...R

Raptor1956: This is the modified code that pads the ON time to 1uS.

Nice work!

Now read the datasheet chapter on timers to discover the proper way to do it.

MorganS: Nice work!

Now read the datasheet chapter on timers to discover the proper way to do it.

Could you be a little more specific -- the two timers that are available "delay()" and "delayMicroseconds()" don't offer the needed resolution to delay, say, 800nS. If there's something more at the hardware level I need to know about perhaps a pointer to the specific section might be helpful as the datasheet is 435 pages with numerous sections on timers and clocks.

Brian

Raptor1956: Could you be a little more specific -- the two timers that are available "delay()" and "delayMicroseconds()"

Those are not the timers that @MorganS was referring to. The Atmega chip contains Hardware Timers that are described in full detail in the relevant Atmega datasheet, One of the those timers (Timer0) is used by the Arduino to create the millis() and micros() timing.

...R

OK, I guess we need to be a bit more helpful than just "read the datasheet!"

One good reference on timers is here: https://www.gammon.com.au/forum/?id=11504 Unfortunately it doesn't address your specific problem in the first 10 pages, so it's still a bit of a slog to read through it.

Part of the problem is that I don't fully understand the timers myself. I can get them to do work for me but some of it is just copy-paste from other people's code.

Maybe I can provide a little overview. (I'm happy to be corrected by the other experts here.)

The hardware timers in the Arduino chips are basically counters that can count without your code having to do all the work. They are usually clocked from the main system clock but through a 'prescaler' so that you can convert the 16MHz rate of the clock down to a more useful timing interval, to get you started on the way to measuring milliseconds.

The timers are small. Timer0 is only a byte so it can only count to 255 before it resets to zero. That is a problem for counting milliseconds as you often want to measure periods longer than a second. So the Arduino framework uses an 'interrupt' at the end of the timer to add to the total count of milliseconds. But the timer is never stopped - it keeps counting while the rest of your code is away doing other things. So you never miss out on counting milliseconds.

The timers are also used for PWM (bear with me, this is getting closer to answering your question.) If you want to drive a motor at 50% power, you could start with the output pin switched on and then when the timer reaches 128, turn the pin off. When the timer overflows back to zero, turn the pin on again. So there's another value that you can give to the timer system which represents that intermediate value. You can do this with an interrupt "hey, let me know when you get to 128 and when you get to zero, ok?" or the timer can do this by itself. The timer can be connected directly to an output pin and, by setting the right options, the timer will turn the pin on and off thousands of times per second with no further interaction from your code. This is what analogWrite() does and this is why that function only works on certain pins - the timers can't do this for every pin.

If the timer always counted up to 255, that would be great but you can't control the speed very precisely just by changing the prescaler. So you can tell it to actually overflow back to zero at any number you choose. You can even give it a different start number instead of zero. That's used for various other purposes like 'phase correct PWM.'

So let's make it drive a stepper at 50kHz. That's one pulse of 1us every 20us. Imagine we had a timer clocked at 1us per step by setting the prescaler. At time=0 it should turn the pin on. At time=1 it should turn the pin off. When it gets to 19, it should overflow back to zero and it should interrupt your main code so that you can add 1 to your count of steps taken. The hardware timer will do this "forever" with no further action. If you want to change the speed, just change the number that the counter counts up to.

Note that you never actually use simple round numbers with timers. To count up to 20, you actually have to count to 19 because 0 is a number. The prescaler never quite gets you exact microseconds, so you have to do a bit of math with 12.5 counts per microsecond or whatever.

MorganS, thanks, that is more helpful. And I get the uncertainty thing about timers and clocks as that is often more of the hardcore programmer/engineer kind of topic. In addition, going that route would necessitate the use of interrupts which is itself somewhat more esoteric then the more pedestrian code most Arduino newbies are inclined to dabble in.

But, looking at it more carefully the idea of 500KHz pulse rates with a controller that only runs at 16MHz is kind of like trying to squeeze 10 pounds of %#*& in a 2 pound bag.

In a prior life working at IBM I'd just order a dedicated servo/stepper control board and drivers to handle upwards of 8 axis at the same time with step rates far beyond the 500KHz limit my driver has with a PC running as a supervisor and not having to handle the timing or pulse generation. That's a far more expensive approach and most real world commercial tools (CNC, robots, etc) are going to use these more expensive and capable systems.

I suppose one way around that would be a dedicated Arduino for each motor with an Arduino running as a supervisor but then you'd need to handle the communications with all the axis level controllers.

Brian

Interrupts aren't all that hard.

I say again: the original chip designers know more than you do about what you need. They already put several of them on the chip. Think of it as a 16MHz processor supervising dedicated hardware modules.

Keep searching online for examples using the timers to run stepper motors.

MorganS: Interrupts aren't all that hard.

I say again: the original chip designers know more than you do about what you need. They already put several of them on the chip. Think of it as a 16MHz processor supervising dedicated hardware modules.

Keep searching online for examples using the timers to run stepper motors.

No, interrupts are not as hard as some make them out, but with only 16MHz to work with interrupts introduce there own performance hit that are more tolerable with a faster controller but less so at 16MHz.

Brian