Playing with Chuck's Rotary table code

Home Model Engine Machinist Forum

Help Support Home Model Engine Machinist Forum:

This site may earn a commission from merchant affiliate links, including eBay, Amazon, and others.

Scott_M

Well-Known Member
Joined
Apr 13, 2013
Messages
425
Reaction score
203
Location
Medina, Ohio USA
I wanted to see how Chuck's code worked with a micro-stepping driver so I rigged this test up using an old stepper and driver from my Tormach mill. This is a "Tordrive 2000" 10 microstep driver and a 640 oz. in. Nema 34 stepper motor. The motor is spec'd at 24-70 volts and 5.5 amps, I am kind of surprised how well it ran at 19 volts. The power supply I am using is a Laptop charger rated at 19.5volts and 6.2 amps.
I modified Chuck's code a little bit to remove the second device. I also set the default delay to 0, with the micro-stepping driver it was running painfully slow with the delay set at 5. And just for fun I changed the "set num of divisions" to use 2 lines of the display.
I know there was some concern that this code was using "floating point" math, I wanted to see where the limits would be.
It looks like it will work for a 40:1 rotary table. But not for a 90:1.
This test was with 80000 steps
200 steps per turn for stepper x 10 microsteps x 40 turn table = 80000.

Short video.
www.sdmfabricating.com/upload_page/ArduinoRotaryTest.MOD
I tried a different file format maybe you Mac guys can see it now ?

Scott
 
I think with some modified code the Arduino can drive a 90:1 table. Well I hope it can. In previous tests, the Arduino could happily drive the microstepper (2000 steps) at the same speed as an ordinary driver (200 steps). I've played briefly tonight and have it running on a Freetronics LCD.

You need to use Timer Interrupts. You can safely replace all of the ints and floats with longs so that alone will speed things up. eg. like this:

Code:
long del = 5L;                            // default delay value
long divs = 0L;                           // Holds the number of divisions
long pos = 0L;                            // Holds the current dividing head position (0 (zero) based)
long lastpos = 0L;			// Last absolute stepper position
long nextstep = 0L;                       // used for calculation of next stepper position
long intsteps =0L;
long laststep = 0L;
long nsteps = 0L;                         // Count of steps to advance stepper
long steps = 0L;
long stepsDiv = 0L;			// Steps per Division
long numSteps = 1000L;                // Number of steps per spindle revolution

Note the use of the L modifier to tell C it is a long value.
Then get rid of the ABS and ROUNDs on the float values where used in the formulas. By using longs, the results will always be a whole number. Like This

Code:
  laststep = stepsDiv * lastpos;
  nextstep = stepsDiv * pos;
  nsteps = nextstep - laststep;


And here is the setup for a timer interrupt stepper driver. If you set the vairable steps to some value, the ISR will drive the stepper that many steps in the background. Note I've left out the loop().

If you look carefully, for each call to the interrupt the stepper pin changes state each call (eg high or low) until steps gets to zero. So steps may actually need to be 2 x what you need the stepper to step.

Code:
#define stepPin 4
#define dirPin 5
#define CLOCKWISE     HIGH
#define ANTICLOCKWISE LOW
int dir = CLOCKWISE;  // Clockwise
int state = LOW;
int steps = 8000;

void setup() 
{
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);

  digitalWrite(dirPin, HIGH);
  digitalWrite(stepPin, state);
  
  Timer1.initialize(40); // set a timer of length in microseconds (or 0.1 sec - or 10Hz
  Timer1.attachInterrupt( timerIsr ); // attach the service routine here
}
 

void timerIsr()
{
    // Toggle LED
  if(steps){
      state = state ^ 1;
      digitalWrite( stepPin, state );
      steps--;
  }
}

This line controls how fast the stepper is pulsed
Code:
Timer1.initialize(40); // set a timer of length in microseconds (or 0.1 sec - or 10Hz
I'll try and insert this code over the weekend
 
Hi Rod

Thanks for your effort on this. I wish I had the programming skill to tackle something like this. I am still having trouble with syntax. It seems I always have the {} braces in the wrong place or I'm missing one.
I'm playing with "if" and "else" now......I'll get there eventually.

Thanks again

Scott
 
In my programming days I used emacs as my editor. It had some nice features for coding:

1) Cursor on a { or } highlighted the match

2) Auto indents using tab key aligned { and } for the proper syntax.

Also would find matching ( and ) the same way.

I suppose more modern editors can do the same things, but once you learned emacs it was a very efficient editor in terms of keystrokes and facility.
 
Thanks Kvom

The Arduino editor also highlights the matching Brace, BUT..... you need to know what that means :( last night I struggled with getting some code to compile and it kept giving me an error " expected } at end of input" There was one, it showed the matching brace at the beginning of the loop. It took me a while to realize that I needed 2 at the end
}
}

one for the last input and one to close the loop. I am plodding along.


Scott
 
For what it's worth, I have a 90:1 Rotary Table, and have Chuck F's sketch running a TB6560 driver with a stepper. I changed the RT divisions in his sketch to match my RT(90*200=18,000). I haven't finished machining the adaptor for the stepper to the RT yet, but it drives the stepper o.k.

Chuck
 
Thanks Kvom

The Arduino editor also highlights the matching Brace, BUT..... you need to know what that means :( last night I struggled with getting some code to compile and it kept giving me an error " expected } at end of input" There was one, it showed the matching brace at the beginning of the loop. It took me a while to realize that I needed 2 at the end
}
}

one for the last input and one to close the loop. I am plodding along.


Scott

Scott, always be consistent with formatting and indenting as you can see it much more easilly. Just get in the habit of putting them in always And use the same convention. I like to put the opening brace at the end of the line but more often today you see it on a line on its own. Use the match brace feature of the editor. When there is only one line of code, they are optional As the compiler can work out what it is doing. One tip is to insert the braces before you fill in the code in between. Sometimes the error will be at the end of the file and the missing brace is much further up the page.
 
Hi Chuck
Yes it will work with full steps. Just keep in mind that accuracy will go down as diameter goes up.
Pi * dia = circumference. Circumference / steps will give you linear accuracy per step.
so on a 4" diameter that will give you about 7 tenths per step. For model work that is probably fine.

The buddy I am looking into this for has a biga$$ RT with an 8" chuck. He want to do 12" diameter. That comes out to .002" per step. Which he is not thrilled about so I am trying to get it to work with microstepping.

Scott
 
The TB6560 will allow micro stepping. I'm waiting until the shop thaws a bit, and I get the adapter finished. Then I'll hook it all up and test with and without micro-stepping. I also have a couple of other driver shields and 2 screw shields to play with.

Chuck
 
Hi Chuck
Yes it will work with full steps. Just keep in mind that accuracy will go down as diameter goes up.
Pi * dia = circumference. Circumference / steps will give you linear accuracy per step.
so on a 4" diameter that will give you about 7 tenths per step. For model work that is probably fine.

The buddy I am looking into this for has a biga$$ RT with an 8" chuck. He want to do 12" diameter. That comes out to .002" per step. Which he is not thrilled about so I am trying to get it to work with microstepping.

Scott

Scott it will be easy. Start by making the changes in my first two code blocks.Also if your stepper driver has 10x more steps, then you need to make the delay 10 x shorter.

Its probably worth explaining what interrupts are. There are two types. One that gets actioned when something happens. eg. a value is received on a port and the other is a timer interrupt hooked to the CPU clock. The arduino has several timer interrupts (I am using Timer1 interrupt as there is a library for it).

So the Arduino has a 16 MhZ clock. So that means 16,000,000 times a second the timer fires and the CPU goes off and does its own internal processing that we don't have to know about as programmers. It all happens in the background.

So with a timer interrupt, we are simply telling the CPU to chain to and run our code every now and again when it is going about its housekeeping. The code has to be short and sweet otherwise there won't be enough time to get it done and crazy things will happen. All we have to do is to decrement a global counter and send a signal to the stepper port. Harder to read but the code has to be highly efficient.

After playing with a Gecko microdrive stepper board (2000 steps per rev), I settled on a timer that fired every 40 microseconds Thats 25,000 times a second. Each time it fires, the stepper signal will change from ON to OFF or vice versa. That means we will get 12,500 steps per second as only the ON's count. So Lets do the maths
12500/2000 steps = 6.25 rev per second
6.25 x 60 = 375 stepper RPM
375 / 90 = 4.16 table RPM

And this is roughly what I observed with a tacho

If I went any faster (which the Arduino is easily capable of), I found that the Stepper stated to miss steps or just vibrate and make awful noises.

Hope this helps, I will post up my code once I get it going. I've been trying to be good and not play with the Arduino during the workng week...
 
Lets descibe what is gong on in here

Code:
void timerIsr()
{
    // Toggle LED
  if(steps){
      state = state ^ 1;
      digitalWrite( stepPin, state );
      steps--;
  }

So this code only fires if there is a value in the global variable steps. eg an efficient way of saying
Code:
if( steps <> 0)

The next line changes the value in the global variable state using bitwise operator OR. An OR operation is only TRUE if the 2 values compared are not the same.
Code:
1 OR 1 = FALSE (0)
0 OR 1 = TRUE (1)

So this
Code:
state = state ^ 1;
is a very efficient way of saying
Code:
if(state == 1){
   state = 0;
}
else{
   state=1;
}

Finally
Code:
 steps--;

is a very efficient way of saying
Code:
steps = steps -1 ;

Hope that all helps understand some code.
 
Well I made some progress and incorporated the Interrupt code into Chucks code. I think it is time to hook up a stepper again.
I added some debugging code that printed values to the serial port while the stepper port was being driven. In the time it took to print
Code:
isrSteps = 3694
to the serial port, the ISR running in the background had sent 234 steps to the stepper port!

I will share this eventually!
 
Scott, always be consistent with formatting and indenting as you can see it much more easilly. Just get in the habit of putting them in always And use the same convention. I like to put the opening brace at the end of the line but more often today you see it on a line on its own. Use the match brace feature of the editor. When there is only one line of code, they are optional As the compiler can work out what it is doing. One tip is to insert the braces before you fill in the code in between. Sometimes the error will be at the end of the file and the missing brace is much further up the page.

Rod , thanks that is very helpful !

And I will study your last post and play with it tomorrow.
It really makes it a lot easier when you are trying to apply something that you want to do rather than some meaningless exercises in a tutorial that you may never use.

I really appreciate your input !

Thanks again

Scott
 
I have programmed in Pascal & C, Pascal used Begin and end statements, C usu { & }

Many times in C the start a function like this -
void function() {

and end it like this -
}

The braces don't line up.
I find it easier to read if I write it this way -
void function()
{

and end it like this.
}

the braces line up.

Always use the same indent amount and line up the braces.

I would write this function -
Code:
void timerIsr()
{
    // Toggle LED
  if(steps){
      state = state ^ 1;
      digitalWrite( stepPin, state );
      steps--;
  }
}
Code:
void timerIsr()
{
    // Toggle LED
  if(steps)
  {
      state = state ^ 1;
      digitalWrite( stepPin, state );
      steps--;
  }
}
 
Last edited:
Well, I pulled out the stepper and fired it up. It is now working with the interrupt code but I am not getting the results I expected as the distance travelled is changing depending on the time delay for the ISR. At first, I thought it might be becasue I was driving the stepper too fast. I'll do a bit more research on the stepper diver. I assumed it would trigger on a change of state from LOW to HIGH, maybe I need to only turn the signal on for a short time period instead of for 50% of the step pulse. It does help to clean up and simplify the code I thought.
 
Attached is a simple working sketch to drive a stepper motor with interrupts.

Just set these variables to suit your situation.

Code:
#define stepPin 1
#define dirPin 2
#define STEPS_PER_REV 2000

This sketch will turn the stepper one full revolution in one direction, wait half a second and then turn it half a revolution in the opposite direction, then repeat ad infinitum.

Sorry, I should have used a #define for the timer delay here. You may need to change this value.
Code:
Timer1.initialize(40); // set a timer of length in microseconds

Have a look at your stepper motor specs and see what frequency in Herz it has maximum torque and what is the maximum frequency it can handle. Remember if you are using a microdrive (like my Gecko) you can drive it 10x faster.

My stepper has max torque at 400 Hz and fades right away out at about 1,000 Hz so that is the ideal range to drive it at. The value of 40 I've used is 12,500 Hz (1250 Hz at the stepper itself with my microdrive) and I can't drive it any faster (35 skips steps) so 50 might be a more conservative value to use as that will give you 10,000 Hz.(1000 Hz at the stepper with a microdrive)

But the identical code plays up in Chucks sketch so I'm still not in the clear.

View attachment ISRBlinkGecko.zip
 
Hi Rod

I can't compile your sketch. I get


ISRBlinkGecko.ino: In function 'void setup()':
ISRBlinkGecko:23: error: 'Timer1' was not declared in this scope

Is the <TimerOne.h> library in the standard IDE ?

Or..maybe I just havn't had enough coffee yet :)

Scott
 
Hi Rod

I can't compile your sketch. I get


ISRBlinkGecko.ino: In function 'void setup()':
ISRBlinkGecko:23: error: 'Timer1' was not declared in this scope

Is the <TimerOne.h> library in the standard IDE ?

Or..maybe I just havn't had enough coffee yet :)

Scott

Scott, Sorry, I must have got the code from here. So long ago!
http://playground.arduino.cc/Code/Timer1

Yesterday I wrote from scratch a complete dividing sketch with the same functionality as Chuck's which is using the Timer1 example above. Very Exciting.

If you are using a Freetronics LCD display and keyboard, This is the menu and form input library I'm using (but I've extended it by adding 2 more input field types). It will save a lot of time.
https://github.com/rweather/arduinolibs

I want to add a few more features like continuous turning, backlash compensation, jogging, set and return to a home position and speed control.
 

Latest posts

Back
Top