Tips for Arduno software for Rotary Table Controllers

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.
Hey Rod, having some issues with accell and decell. Am trying to figure out how you are handling this. Is it just a ratio between min and max hz ?

I currently have my max set at 12500 and min at 7000.
Should I lower the min to get longer accell and decell or do I need to add steps to the "ramp up" and "ramp down" definition ?

Scott

Edit: Just changed min to 4000 and it seems to be working much better.

I will have a full report on this project in a few days. It came out reeeealllllly nice !
 
Last edited:
Scott, sorry for being a bit slow. I have not got back to this project. My foray into steppers got me in and I've been designing a CNC plasma cutter...

I found it needed a bit of trial and error. Setting the start speed too low did not seem to be a good idea. The ramp up worked but it was really slow getting going...

Basically, it starts out by working out how many steps (call it X) it needs to do to get from 7000 to 12500 and changes the frequency every time it does a step. Once it gets to 12500 it stays at that speed until It gets X steps away from the finishing position and the slows the frequency down each pulse until it gets back to 7000 on the very last step of the movement.

If the distance is short, and it can't get up to 12500 and back down again, maybe it only has enough room to get to 9000 and back to 7500 again, it will never reach the max speed of 12500.

I did link to a pdf file from TI that explains this. Worst case set the start and end speed to be the same.
 
Thanks Rod

It seems like if the spread between min and max is large it is an issue. I think I may lower Max just so I can lower Min some more.
This is a 40:1 Dividing head so it turns plenty fast. It is a like new 50's vintage "Cincinnati" It is HUGE ! My buddy is putting an 8" 3 jaw on it.
I will post a build log on it next week when I get down to my buddy's and get some video of it. Here is a pic of the top of the Arduino box.

I can't thank you enough for all you did on this project !! My project has turned out really well because of it.

Scott

P1030677.jpg


P1030673.jpg
 
Scott, that looks great. Keep the pics coming.I'm really pleased to see someone run with my contoller hardware.

From talking to a mate, even MACH3 requires some experimentation with ramp up and ramp down parameters. And yes, I found much the same thing with the settings. I would keep the start speed as high as I could for reliable starting without loosing steps.
 
I've been designing a CNC plasma cutter...

I was in on the design and build of a largish CNC plasma cutter a couple of years ago. The one piece of advice I can give is that you really need a good automatic torch height controller.

You probably already knew but just in case you didn't...
 
I was in on the design and build of a largish CNC plasma cutter a couple of years ago. The one piece of advice I can give is that you really need a good automatic torch height controller.

You probably already knew but just in case you didn't...

Cogsy, thanks. I had worked that out. I have not given a thought to the electronics yet. Been trying to teach myself 3D design and have almost finished the gantry which will be laser cut and welded with locating tabs to position everything. All because this software project got me in and I happened to have a BOC 40 amp plasma cutter here! One good thing is that it has a Thermo Dynamics cutting torch.
 
Just a quick hello and a big thank you!

I have just installed v10 without any major problem. The only annoyance I had were the keys on my DFRobot LCD shield (which looks like a SainSmart). My quick-fix was to replace the #define mapButton in LCD.cpp with a simple function. Certainly a tad slower than the array[] lookup, but so far I can't see any problem from that.

A quick question (or suggestion) : Is it possible to change the speed while in continuous mode ?

Again a big hooray to Rod and this project !

My code for LCD.cpp (uncomment the #define mapButton and buttonMappings[])

Code:
// map the buttons
int mapButton(int value)
{
  /* values depend on the version of the lcd board */
  if (value > 1000) return LCD_BUTTON_NONE;
  if (value < 50)   return LCD_BUTTON_RIGHT;
  if (value < 195)  return LCD_BUTTON_UP;
  if (value < 380)  return LCD_BUTTON_DOWN;
  if (value < 555)  return LCD_BUTTON_LEFT;
  if (value < 790)  return LCD_BUTTON_SELECT;
  return LCD_BUTTON_NONE;  // when all others fail, return this...
}
 
Just a quick hello and a big thank you!

I have just installed v10 without any major problem. The only annoyance I had were the keys on my DFRobot LCD shield (which looks like a SainSmart). My quick-fix was to replace the #define mapButton in LCD.cpp with a simple function. Certainly a tad slower than the array[] lookup, but so far I can't see any problem from that.

A quick question (or suggestion) : Is it possible to change the speed while in continuous mode ?

Again a big hooray to Rod and this project !

My code for LCD.cpp (uncomment the #define mapButton and buttonMappings[])

Code:
// map the buttons
int mapButton(int value)
{
  /* values depend on the version of the lcd board */
  if (value > 1000) return LCD_BUTTON_NONE;
  if (value < 50)   return LCD_BUTTON_RIGHT;
  if (value < 195)  return LCD_BUTTON_UP;
  if (value < 380)  return LCD_BUTTON_DOWN;
  if (value < 555)  return LCD_BUTTON_LEFT;
  if (value < 790)  return LCD_BUTTON_SELECT;
  return LCD_BUTTON_NONE;  // when all others fail, return this...
}

Thanks for feedback from a happy customer. If you modify the goConTurn procedure, you may be able to add new case statements for the up and down arrows to change the frequency.

If you have a look at
goSetSpeed() you will see it incrementing/decrementing in 5% intervals and calculating a value with
Code:
int speedPct = (int)((((float)((float)globals.maxStepperHz - (float)globals.minStepperHz) / (float)(Operating_Hz - globals.minStepperHz)))*100.0);

So maintain a similar variable and increment on up arrow and decrement on down arrow.

and then setting the speed with a call to
Code:
setSpeed(speedPct);

Note there is not much dynamic memory left.... if you get erratic results you have a memory overflow condition and you will need to reduce the size of static strings (ie. Menu entries)
 
Actually, I've got that a bit wrong. Speed is determined by the variable Operating_Hz
The first line quoted in my last post calculates the percentage of the range that Operating_Hz is currently set to. (between the Minimum and the maximum)
So do this calculation on entry to the goConTurn() procedure to get the current percentage and increment/decrement by 5% with up and down buttons and then call setSpeed()
 
Thanks for feedback from a happy customer.

That's not difficult. This is excellent code and a just-right solution.

If you modify the goConTurn procedure, you may be able to add new case statements for the up and down arrows to change the frequency.

Yes, that's what I thought to do.

Note there is not much dynamic memory left.... if you get erratic results you have a memory overflow condition and you will need to reduce the size of static strings (ie. Menu entries)

I have seen that space is getting really, really tight. But I have a couple of things in my head which will need much more than available, so I stripped out all the linear stuff, the devices, the safe to eeprom. My configuration is now done with a couple of #defines at the beginning. It's clearly not as flexible and configurable like yours.

My intended changes include:
- change speed within all different menus (not just continuous)
- add rotary enoder for direct turning
- add one of those IR remote controls
- maybe show RPM while turning (calculated, not measured)

I still remain true to all your core routines (apart from kicking out the globals variable) so we could still walk in parallel.

<><><><><>

Actually, I've got that a bit wrong. Speed is determined by the variable Operating_Hz

Well, no. Operating_Hz currently isn't used anywhere. At least not in v10. I have written a long reply regarding this, but deleted it. Basically this is the central function, as far as I can tell:

Code:
void setSpeed(int spdPct)
{
  globals.maxStepperHz =  (long)((float)( Operating_Hz - globals.minStepperHz) * (float)spdPct/100.0) + globals.minStepperHz;
  globals.timerDelay = HzToTimeDelay(globals.maxStepperHz);
  Timer1.setPeriod(globals.timerDelay);      // set a timer of length in microseconds
}

You are modifying and operating around globals.maxStepperHz. Operating_Hz is just set once in setup() and never touched again. Because you are ramping to and from globals.maxStepperHz, and because Operating_Hz holds the MAX_FREQUENCY, it still works. So all is good functionality-wise, but by accident, so to speak. ;)

Again thanks for sharing.
Happy days to you, Rod!
 
Another bug (?)

Apart from the Operating_Hz variable which probably should be used here as well, it seems to me that start and target needs to flip, otherwise rampSteps gets negative, which presumably stops the stepper immediately instead of ramping down.

I am still not very clear about this frequency thing; so maybe this is as intended and not a bug.

Code:
void rampDown(void)
{
  startDelay =   HzToTimeDelay(globals.minStepperHz);          
  targetDelay =  HzToTimeDelay(globals.maxStepperHz); 
  rampSteps = ((startDelay-targetDelay) * globals.microSteps);  
  startRampDown = rampSteps - 3L;      // Steps to start ramping down - steps count down so we start at a high #
  endRampUp =  rampSteps -1L;                      // We don't want to Ramp Up as we are supposed to be moving
  sDelay = targetDelay;
  steps = rampSteps + 1L;              // Run enough steps to stop gracefully
}

Hope you don't mind that I throw all this at you. :D
 
You are modifying and operating around globals.maxStepperHz. Operating_Hz is just set once in setup() and never touched again. Because you are ramping to and from globals.maxStepperHz, and because Operating_Hz holds the MAX_FREQUENCY, it still works. So all is good functionality-wise, but by accident, so to speak. ;)

Again thanks for sharing.
Happy days to you, Rod!
Coming back to me now. Maybe I started to change the code to use Operating_Hz but changed my mind (ie. got confused.)

The rampDown() and rampUp() procedures need the max and min StepperHz values. They can be changed at will until they are changed in setup or the Arduino is rebooted (which reads the values from eeprom again).

The most valuable contribution would be to recode the menu library so that it uses PROGMEM variables. This would allow the variables to live in the codespace and never get loaded into the 2k variable storage area on bootup. This (Simple?) change would solve ALL of your code space issues.
 
Another bug (?)

Apart from the Operating_Hz variable which probably should be used here as well, it seems to me that start and target needs to flip, otherwise rampSteps gets negative, which presumably stops the stepper immediately instead of ramping down.

I am still not very clear about this frequency thing; so maybe this is as intended and not a bug.

Code:
void rampDown(void)
{
  startDelay =   HzToTimeDelay(globals.minStepperHz);          
  targetDelay =  HzToTimeDelay(globals.maxStepperHz); 
  rampSteps = ((startDelay-targetDelay) * globals.microSteps);  
  startRampDown = rampSteps - 3L;      // Steps to start ramping down - steps count down so we start at a high #
  endRampUp =  rampSteps -1L;                      // We don't want to Ramp Up as we are supposed to be moving
  sDelay = targetDelay;
  steps = rampSteps + 1L;              // Run enough steps to stop gracefully
}

Hope you don't mind that I throw all this at you. :D

I ran heaps of tests with print functions in the main loop to monitor the ISR routine to trace what was going on while ramping up and down. It is not intuitive. In the dividing functions, I am confident there are no bugs...

This bit could be buggy as what I am attempting to do is to calculate the number of steps required to ramp up and ramp down for the continuous turning procedure to avoid missed steps and loss of position. Read on and hopefully, this will become clearer.

A Frequency of 1 Hz means 1 on and off cycle per second. This needs 2 cycles (1 for on and 1 for off) through the ISR routine (last procedure in the file) . Think of AC power which is 50Hz.. (50 complete cycles per second) So rather than stuff around with a short pulse every now and again which is how most Arduino stepper drivers work, while the stepper is operating, the pulse goes on and off for an equal time period. This is how the data for a stepper motor is presented (eg data sheets quote frequencies).

So now we get to the ramp down and ramp up functions. Steppers generate max torque at lower speeds. Starting at a high speed/low torque might lead to missed steps (and I had a lot before I got it to run reliably). So the idea according to the Texas Instruments documentation I referenced is to start slow and ramp up by adding 1 extra step through each iteration of the ISR until it reaches the Max operating frequency. (lets just assume that takes 1000 pulses. So before we stop, we also want to ramp down as at high speed the lack of torque could cause the stepper to overrun and run for more steps than it should. So before the end point, we ramp down 1000 steps in exactly the reverse manner.

So finally, what happens if say the total movement is less than the sum of the ramp up and ramp down interval? Simple really. If say the total steps to move required was 1000, we would never get to operating speed. We'd ramp up to 500 steps, never reach operating speed then ramp down 500 steps.

The other thing that is happening in the ISR is that the number of steps from the home position is maintained at all times. Remember that you have no control over the ISR, it is just triggered by the CPU 16,000,000 per second... (16 Mhz). We must be short and sweet in an ISR as we don't have much time to do stuff!
 
Last edited:
Another bug (?)

it seems to me that start and target needs to flip, otherwise rampSteps gets negative, which presumably stops the stepper immediately instead of ramping down.


I just re-read your comment above As I said, it is not intuitive. The starting delay is bigger than the target delay because at this level we are counting the number of timer pulses between steps. Slow speed (start) = long delay. Target (fast) speed = short delay. Does that make sense?
 
Coming back to me now. Maybe I started to change the code to use Operating_Hz but changed my mind (ie. got confused.)

In that case you could do : "%s/Operating_Hz/MAX_STEPPER_HZ/g" and fix the two syntax errors you will get at compile time. Saves a byte or two ;)
 
Ron, another (probably stupid) question:

I see you are using Timer1.initialize() in setSpeed() and in timerIsr() instead of Timer1.setPeriod(). Any special reasons for that? Surprisingly, the Timer1 inline-code says initialize() stops the timer.

I have added the speed change into continuous turning. I particularly like that I now can actually see and hear the speed, instead of trial-and-erroring it in percent-steps.

The solution has three parts. It also needs two new variables.

Code:
volatile long smoothRamp = 0L; // Steps to move to next speed (in continous mode)
volatile long smoothDir = 0L;  // speeding up or down in smoothRamp

First: Add buttons to goConTurn() as you have suggested:

Code:
      case LCD_BUTTON_UP:
        rampTo(globals.maxStepperHz + 100);
        break;
        
      case LCD_BUTTON_DOWN:
        rampTo(globals.maxStepperHz - 100);
        break;

Second: Add a function to ramp to new speed.

Code:
void rampTo(long nextHz)
{
  /* should have some precautions against going too slow or fast */

  if(nextHz > globals.maxStepperHz) {
    // get faster
    startDelay =   HzToTimeDelay(globals.maxStepperHz);
    targetDelay =  HzToTimeDelay(nextHz);
    smoothDir = -1;
  } else {
    // get slower (or stay the same)
    startDelay =  HzToTimeDelay(nextHz);  
    targetDelay =   HzToTimeDelay(globals.maxStepperHz);
    smoothDir = +1;
  }

  globals.maxStepperHz = nextHz;
  smoothRamp = (startDelay - targetDelay) ;
}

Third: Add some code to timerISR(), right before your ramping if()'s

Code:
      if(smoothRamp > 0) {
        stepcount++; stepcount++;
        stepmod = ((long)stepcount % (long)MICRO_STEPS);
        if (stepmod == 0) {
          smoothRamp--;
          sDelay += smoothDir;
          Timer1.initialize(sDelay); // adjust timer 1
        }
      }


Cheers !
 
Torfilli,

It is a great enhancement that you have coded. I did not think of it.

I had no idea I was doing something with the timer that was no quite right. But it worked :)

Glad you have worked out the ramping up and down. I would have to revisit the code but there may be a more efficient way to code the feature but after a couple of glasses of red wine with dinner , it presently eludes me. But if it works :)
 
Hi Torfilli

I too use a copy of a sainsmart LCD and my left button doesnt operate as it should

I had the same problem with Chuck Fellows code but easily found the line where
I could change the ADC value that equated to "left"
You post explains how to change LCD.cpp to do the same thing but I dont really
understand how to do it ( its an age thing ). Is it possible you could upload a copy of your LCD.cpp so I can see exactly what and where you have changed it
Apart from the left button Rods code is working fine for me

Regards
Don
Just a quick hello and a big thank you!

I have just installed v10 without any major problem. The only annoyance I had were the keys on my DFRobot LCD shield (which looks like a SainSmart). My quick-fix was to replace the #define mapButton in LCD.cpp with a simple function. Certainly a tad slower than the array[] lookup, but so far I can't see any problem from that.

A quick question (or suggestion) : Is it possible to change the speed while in continuous mode ?

Again a big hooray to Rod and this project !

My code for LCD.cpp (uncomment the #define mapButton and buttonMappings[])

Code:
// map the buttons
int mapButton(int value)
{
  /* values depend on the version of the lcd board */
  if (value > 1000) return LCD_BUTTON_NONE;
  if (value < 50)   return LCD_BUTTON_RIGHT;
  if (value < 195)  return LCD_BUTTON_UP;
  if (value < 380)  return LCD_BUTTON_DOWN;
  if (value < 555)  return LCD_BUTTON_LEFT;
  if (value < 790)  return LCD_BUTTON_SELECT;
  return LCD_BUTTON_NONE;  // when all others fail, return this...
}
 
Is it possible you could upload a copy of your LCD.cpp so I can see exactly what and where you have changed it.

Hi Don,

No problem at all; here it is.

I have commented the lines 323-327 in the original code (from the package that Rod has uploaded elsewhere here in the thread) and have added immediately afterwards my replacement-function to return a keycode from the ADC value. No magic involved.

The values in the if() statements vary from version to version, from resistors to resistors, of the shield so my version may not be cut-n-paste compatible. Just plug in the values you already have, and you should be good to go! If not, yell! and we'll find a way.

Enjoy life!

Torfilli

View attachment LCD.zip
 

Latest posts

Back
Top