Also: Embedded CNC for other uC
This code converts a line in 3 dimensions into the pulse and direction signals necessary to make a 3 axis stepper motor driven CNC machine follow that line while maintaining predefined maximum acceleration and maximum velocity parameters.
/*
3D Line to Stepper Axis Pulses
by JamesNewton@MassMind.org (please do email comments, corrections, bug reports, etc...)
Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
Purpose: Convert a line in 3 dimensions into the pulse and direction signals necessary to make a 3
axis stepper motor driven CNC machine follow that line while maintaining predefined maximum
acceleration and maximum velocity parameters.
Limitations: Must fit in a small microcontroller. e.g. PIC 16F690 with 4K of code space and 256
bytes of RAM.
DONE:
- steps and directions on port pins... Motors turning!
- figure out how to adapt Bresenham to 3d... Appears to work
- code up some velocity and acceleration control.. Could be done
- figure out why mv and ma are not being returned to their original values. Is sd not being
calculated correctly? yeah, sd needed to just be incremented and lots of other tweaks...
TODO:
- acceleration ramping is NOT correct. Must incorporate the fact that a given change in the delay
between steps has more effect when the delay is small, as compared to when it is large.
E.g. motor spinning at 500uS between pulses, is reduced by 250uS, that doubles the motor speed.
But when /increased/ by 250uS, that does NOT halve the motor speed. Formula may be:
starting velocity * (1/sqrt(x))
where x is the step... or the time interval... not clear yet.
- simulate, debug, test, etc... with xaxis=bigaxis
- looks like sd is always just half of i?
- change each axis variables into a structure so they can easily passed as parameters
- handle y and z being the big axis
- decide how the starting and goal positions are going to get set. Minimal G code interpreter?
- decide how the max velocity and acceleration settings are getting set. G code?
- clean up order of declarations, subroutines, move things into include files and all that other
crap C programmers are expected to do for some strange reason.
LINKS:
http://forums.reprap.org/read.php?12,9459 Arduino G code to stepper. See user.cpp file.
Compiler notes: Using >>1 throughout in place of /2 because some compilers are seriously too stupid
to optimize a divide by two in to a right shift.
*/
// OPTIONS
#define DEBUG
/* When defined, printf's the step,stopping distance, x,y,z and velocity and acceleration
to the console */
#define ACCELERATION
/* When defined, the code will attempt to accelerate from STARTING_VELOCITY to MAXIMUM_VELOCITY*/
#define RAMP_ACCELERATION
/* When defined, the code attempt to smooth the change in acceleration and so the G-Force
experienced by the load being moved through the lines path will build and fall over some time.
When not defined, the acceleration is a fixed value so the G-Force at the load will jump from
0 to a fixed value as soon as
motion begins. */
#define STARTING_VELOCITY 800
//mS between steps
#define MAXIMUM_VELOCITY 20
//ms between steps
#define MAXIMUM_ACCELERATION 20
//maximum acceleration as change in uSecond delay time between steps.
#define MAXIMUM_DELTA_ACCELERATION 10
//maximum change in acceleration. Must be less than MAXIMUM_ACCELERATION
#include <stdlib.h>
#ifdef DEBUG
#include <stdio.h>
#endif
#define STEPX RA1
#define STEPY RA2
#define STEPZ RA4
#define STEPA RA4
#define DIR RA5
typedef enum {cw=0,ccw=1} tdirection; // so clock wise is false
typedef enum {xaxis=1,yaxis=2,zaxis=4,aaxis=8} taxis;
#define tcoordinate int
/* tcoordinate must be a type which can hold the range of positions through which the system
will travel when expressed as individual steps. */
//Current position.
// Must be maintained from one movement to the next. step() does this job
static tcoordinate xp; //current position in the X axis.
static tcoordinate yp; //current position in the Y axis.
static tcoordinate zp; //current position in the Z axis.
//current direction for each axis, assume forward(true)
tdirection xd=cw; //X direction
tdirection yd=cw; //Y direction
tdirection zd=cw; //Z direction
void step(taxis anaxis, tdirection adir, tcoordinate *apos) {
// DIR = 0;
STEPX = STEPY = STEPZ = STEPA = 0;
__delay_ms(8); //otherwise you can't see the pulse.
DIR = adir;
/* if (cw==adir) {
(*apos)++;
}
else {
(*apos)--;
}
*/ (cw == adir) ? ((*apos)++) : ((*apos)--);
if (anaxis & aaxis) {STEPA = 1;}
if (anaxis & zaxis) {STEPZ = 1;}
if (anaxis & yaxis) {STEPY = 1;}
if (anaxis & xaxis) {STEPX = 1;}
__delay_ms(8); //otherwise you can't see the pulse.
STEPX = 0; //X LED is 2 GND, so turn it back off now
}
//goal position
tcoordinate xg; //X axis goal position
tcoordinate yg; //Y axis goal position
tcoordinate zg; //Z axis goal position
/* Limits. We are assuming that all axis have the same max values, which is unlikely, but
simplifies the code, and can be functional, as long as we limit all axis to the weakest
axis. */
#define sv STARTING_VELOCITY
//starting velocity as uSecond delay time between steps.
short mv = MAXIMUM_VELOCITY;
//maximum velocity as uSecond delay time between steps.
#ifdef RAMP_ACCELERATION
short ma = MAXIMUM_DELTA_ACCELERATION;
//maximum acceleration
#define mda MAXIMUM_DELTA_ACCELERATION
//maximum change in acceleration
#else
#define ma MAXIMUM_ACCELERATION
//maximum acceleration as change in uSecond delay time between steps.
#endif
//velocity of each axis, expressed in timer ticks between steps.
unsigned short xv=sv; //velocity of the X axis
unsigned short yv=sv; //velocity of the Y axis
unsigned short zv=sv; //velocity of the Z axis
void posgoal2stepdir(tcoordinate *p, tcoordinate *g, tcoordinate *s, tdirection *d) {
/*translate our current position, and the goal position, into the number of steps required to
reach that goal and the direction to travel. */
signed tcoordinate i;
i = *g - *p ; //how far do we have to move?
*d = cw;
if (i<0) { //is the goal less than the current position?
*d = ccw; //set the direction to reverse(false)
i = (i>0?i:-i); //change to a count
};
*s = (tcoordinate)i; //convert to a distance in the coordinate plain.
}
void main() {
/* stopping distance. This keeps track of how long we expect to take to get stopped once we get
going. */
tcoordinate sd = 0; //stopping distance
//number of steps required
tcoordinate xs; //X axis
tcoordinate ys; //Y axis
tcoordinate zs; //Z axis
init();
//some test values
// xg=10;yg=5;zg=2;
// xp=20;xg=10;yg=5;zg=2; //
xp=20;xg=100;yp=5;yg=50;zg=2; //
//figure out distance and direction for each axis
posgoal2stepdir(&xp,&xg,&xs,&xd);
posgoal2stepdir(&yp,&yg,&ys,&yd);
posgoal2stepdir(&zp,&zg,&zs,&zd);
//find which axis has the largest move
taxis bigstep = xaxis;
if (ys>xs) bigstep = yaxis;
if (xs>xs) bigstep = zaxis;
/* Now we know which axis needs to travel the farthest. So that one should be
the one we step most often. The other two axis get stepped when the error in
their position exceeds one step as per the Bresenham algorithm.
http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
The other axis can not possibly exceed the velocity and acceleration of the
big axis and should be a scaled version of that maximum.
*/
if (xaxis == bigstep) {
//Bresenham error
signed tcoordinate ye; //Y axis
signed tcoordinate ze; //Z axis
ye = ze = (xs >> 1); //error for each smaller distance starts as half the largest distance
#ifdef DEBUG
puts("step:stop\tx\ty\tz\tvel\tacc\r");
#endif
for ( tcoordinate i = xs; i > 0; i--) { //i counts down steps to the end of this movement
step(xaxis, xd, &xp); // we always move the big axis and step does dir and pos
//pretty much pure Bresenham here except step() manages direction so we dont have to.
ye -= ys;
ze -= zs;
if (ye < 0) {
step(yaxis, yd, &yp);
ye += xs;
};
if (ze < 0) {
step(zaxis, zd, &zp);
ze += xs;
};
#ifdef ACCELERATION
//figure out how long to wait for the next step by considering position, velocity, and acceleration
if ( i > sd ) { //unless we are nearing the end...
if ( xv > mv ) { // unless we are at max velocity
// Note: _greater_ because smaller values mean less delay and so faster
xv -= ma; // reduce delay to increase speed by max acceleration.
#ifdef RAMP_ACCELERATION
if (xv >= (mv+((sv-mv)>>1))) { // greater because smaller is faster
ma += mda; //increase acceleration,
} // we are not yet half way to max velocity
else { // xv is now less than twice mv
ma -= mda; //decrease acceleration,
} // we are reaching max velocity
#endif
sd++; // more speed means more distance needed to stop
}
}
else { //we are nearing the end
xv += ma; // increase delay to reduce speed by max (de)acceleration.
#ifdef RAMP_ACCELERATION
if (xv <= (sv-((sv-mv)>>1))) { // less because smaller is faster
ma += mda; //decrease deceleration,
} // we are coming to a halt.
else { // xv is now less than twice mv
ma -= mda; //increase deceleration,
} // we are still nearer max velocity
#endif
} // end if ( i > sd )
#endif //ACCELERATION
#ifdef DEBUG
printf("%04d:%04d\t%d\t%d\t%d\t%d\t%d\r",i,sd,xp,yp,zp,xv,ma);
#endif
delay(xv); //pause to give the motors time to get to the new step
} // end for
} // end if (xaxis=bigaxis)
//And all that again for the case of Y being the bigaxis or Z...
if (yaxis == bigstep) { };
//...
if (zaxis == bigstep) { };
//...
//Or we could make a subroutine...
}
| file: /Techref/microchip/CNC16F-jn.htm, 10KB, , updated: 2016/2/16 00:17, local time: 2025/10/23 22:21,
216.73.216.20,10-1-5-169:LOG IN
|
| ©2025 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://massmind.ecomorder.com/Techref/microchip/CNC16F-jn.htm"> PIC Microcontroller 16F based Computer Numerical Control </A> |
| Did you find what you needed? |
Welcome to ecomorder.com! |
|
The Backwoods Guide to Computer Lingo |
.