# Thread: PIC-Based Step Generation With Linear Acceleration?

1. I'm not sure I understand what you are saying...

1. If you just take the parameters for the slowest axis and apply those as the limit for ALL axis, in all cases, then you can't possibly exceed the profile even for the slowest axis.

2. You can do the same thing on a per-move basis: Use the parameters for the axis which will be moved farthest as the limit, then check the other axis by calculating the percentage of their movement vs the primary axis times the current limit and reduce the limit if necessary.

E.g. if X is moving the most, start with the limits for the X axis... say max vel of 20 and acc of 10. Now lets say Y is moving half the distance of X. So our current limit will move Y at 40 and 20. If Y is actually limited to 50 and 20, change our limits to 50 and 20. And so on to check Z.

The difficulty with this approach is that we have to do a division, but we only have to do it once, before the actual move, so that isn't so bad.

The advantage is that if your slower axis isn't moving much, it won't hold back the faster axis.

Anyway, I expect the user to do this work and send an appropriate rate command before sending the movement. I don't see any reason to work that into the code inside the controller... Do you?

2. 1. Then you will end up with a machine that is not moving the way the user expects it to do. Unless you set this to ALL axis all the time (you will never see any difference while working with different axis, but then again, you will be constraining the user in how his machine is to be made).

Another example.

X: 50 Y: 2
Now if Y is also the slowest axis to accel (Lets say X has 50 and Y has 2), you will end up with an X axis that is moving A LOT slower than what it can, and the same would be for the Y axis. In this case, using the X axis profile would be better even though it's not the slowest.

What interface will this thing have? Through a computer?

3. Originally Posted by The Blight
RomanLini, you should look up the bresenhams routine for linear moves.
...
Umm, yeah I've been using Bresenham in various ways since about 1989 when I was doing 2D graphics programming on early PC apps.

James, I don't see the problem with accelerations per axis either. You just check during the pre-processing if any axis has a different accel profile and (if needed) adjust all axes so all are in-bounds.

I checked your code, it's a pretty neat standard Bresenham although it's not optimised (yet) for PICs. I compiled your code for PIC 16F690 and checked the assembler output for timing.

Here are some results;
Code:
//=============================================================================
void step(taxis anaxis, tdirection adir, long *apos)  // function call takes 8 insts
{
//	DIR = 0;
STEPX = STEPY = STEPZ = STEPA = 0;     // 25 insts (silly compilers!)

delay_us(5);                           // 25 (assuming 5 MIPS at 5 uS)

(cw == adir) ? ((*apos)++) : ((*apos)--);   // 46 to 80 insts
if (anaxis & aaxis) {STEPA = 1;}       // 3
if (anaxis & zaxis) {STEPZ = 1;}       // 3
if (anaxis & yaxis) {STEPY = 1;}       // 3
if (anaxis & xaxis) {STEPX = 1;}       // 6

delay_us(5);                           // 25

STEPX = 0;	//X LED is 2 GND, so turn it back off now     // 1
}	                                   // return is 2
// total execution time for function is about 153 to 187 insts
So the step() function currently takes 153 to 187 PIC insts but can be refined in a number of ways.

You main Bresenham loop looks like this;
Code:
// James bresenham loop;
for (i = xs; i > 0; i--)          // 24 insts
{
// make the bres pulses
step(xaxis, xd, &xp);	        // 153-187 insts

ye -= ys;                     // 30
ze -= zs;                     // 14
if (ye < 0) {                 // 19
step(yaxis, yd, &yp);       // 153-187 insts
ye += xs;                   // 14
};
if (ze < 0) {                 // 19
step(zaxis, zd, &zp);       // 153-187 insts
ze += xs;                   // 14      (695)
};

// calc the delay period...
if ( i > sd )                 // 16
{
if ( xv > mv )              // 8
{

xv -= ma;                     // 12
if (xv >= (mv+((sv-mv)>>1)))  // 33
{
ma += mda;	                // 7
}
else
{
ma -= mda;	                // 6
}
sd++;	                        // 7     (89)
}
}
else    //we are nearing the end
{
xv += ma;	                      // 12
if (xv <= (sv-((sv-mv)>>1)))    // 33
{
ma += mda;	                // 7
}
else
{
ma -= mda;	                // 6
}
}

// do the actual step delay
delayJ(xv);	      // min 8 insts         (695+89+8+?) (792)
}
So assuming worst case timings for most of the if > tests it looks like about 792 PIC instructions per step generated, which with a 20MHz PIC at 5 MIPs will hit the wall just under 2 revs per second. Obviously there is a LOT you can do to speed that up, but there is also a lot to be fixed or added (see below)

The big problems are more complex.
There's no provisions for YZ +1 overruns etc, it relies on the Bres and roundings etc that these 2 won't overrun, that's probably not a good idea. It's better to deal with absolute values and ensure that exactly the right amount of steps are generated for each axis, always.

There is no timing compensation, so the step period depends how long the code takes, then your step period is added using delay(ms). So the step perriod is variable and even worse is non-linear as the faster the motor goes the more the code time affects your step period. That should be done using a PIC timer, ideally.

There is a lot of preprocessing between moves, and no easy way to do the preprocessing *while* the move occurs. That's pretty important, including receiving a multi-byte serial command etc for the next move. Otherwise you have a big delay in between each move for this; send move done confirmation serial, receive new serial move command, preprocess move command. That gives you a "stutter" between multiple short moves like a curve made from tiny straight lines, which will probably stall your steppers. This is a big issue.

You haven't got fine enough speed or period resolution. To make the machine run at the right movement speed you need to be able to fine tune the speed as it depends on the XYZ interpolation, ie; hypotenuse distance/time not axis distance/time. Unless you want to deal with hypotenuse and square roots etc in real time you need to pre-compensate before the move (ideally in the PC) and that needs very fine control of axis speeds.

Finally there's a big problem with the system used for timing the accel ramps. With a fast machine you need to change the accel ramp (step period) every 1mS or so, and for each 1mS ramp "zone" you will be generating between 1 and hundreds of steps over that 1mS period. That looks like it would require a total re-write of your system as yours is designed to have 1 ramp "zone" for every step generated.

And, not to be mean but you are not doing "linear acceleration", you are linearly modifying the period, not the speed. To get linear acceleration you need to linearly increase the speed over real world time (say every 1mS), then derive the period from 1/speed which will require some type of 16bit or 32bit division on the fly, OR a lookup table.

(Added) I hope none of that sounds harsh James, I have known you for years and consider you a good friend, I just thought it was best to be blunt and get stuck right in to your code.)

4. Hi Roman,
Really really impressed by james and your work...You guys are talking on the topic where 99% of cnczoner are absolutely unaware...
Roman, You are software programmer and a good electronics engineer.. What else you are?
Really a big fan of you.

Thanks for sharing your knowledge with us, although it is almost difficult to understand for the dumb person like me.
Regards

5. @Blight, Yeah... I still don't see that as a huge problem. Again, the speed of each move can be set for the slowest axis in that move by the code that feeds the unit. Even if you just set the worst case for all axis, don't most machines move each axis at more or less the same speed? Certainly there are some differences, but in general the mills I've worked on are very close to the same. And worse comes to worse, calculating the max speed for each move inside the unit isn't that difficult. I'll bet you could help whip up that code, right?

The point of this code is to support newer popular operating systems like Windows which are NOT reliable (lol... or at least not deterministic) in terms of timing changes on the parallel port pins, and which are, more and more, running on hardware without parallel ports. The input can be serial or USB (which is basically still a serial port) via either of these:
RLC_3:USB-TTL serial converter
RLC_1:RS232-TTL level converter in a DB9 backshell
connected to this:
Break Out Board, Logic Power Regulator, Relay Driver, Indicators, Switches, Microchip PIC, LCD Panel, Current and Temp sensors. or a future revision of it.

And the software on the host might be hyperterm (sending simplified G-code processed from real G code by custom software), a custom package, or eventually even a plug in for Mach 3. I'm hoping I can squeeze in enough of a G code interpreter that basic output from design programs could be directly piped over and still work... could be a pipe dream, but it's fun to try, especially with some help.

@Roman, yep, no doubt the step routine needs optimizing... I'll probably just set the appropriate step bits in that routine, then clear them before the step delay at the end of the main loop.

And yes, that final delay needs to be on a a timer. It will actually start counting down at the top of the loop, then be adjusted at the end by single path, time consistent code as needed. If the timer has already passed the desired delay... then the unit is working at it's max speed and will limit the motion of the mill.

The C compiler I'm using (free Hitech) optimizes NOTHING and I would code all this in asm from the start but A) I don't want to take the time at this point and B) I'd like the code to be more portable to other uC's and applications. I do think with the optimization that even a little PIC will be able to manage useful speeds. Perhaps I'm wrong, but then the code can easily be ported to a faster uC.

What is a "YZ +1 overrun"? Can that really happen in Bresenham? My understanding is that this was the whole point of the method... it always lands at the exact correct point.

If there /are/ cases where Bresneham doesn't get to exactly the right end point, that can be compensated as a secondary (very short) movement, and so should not affect the timing of the main move.

I can see how multiple short moves would be very slow... but I don't see how they could possibly stall the steppers... If I had the code space (and I probably won't in the little PIC, but I might in a larger one) I would implement a separate move planner for circles and then process the G codes for circular motion.

Your comment about ramp zones completely confuses me. I'm calculating the ramp between every single step, much less every mS.

Re: "linear acceleration": Please look at the code again... not only am I changing the period between steps (thereby changing the velocity) I am ALSO changing the /change/ in period (thereby changing the acceleration). Those changes are being recalculated with each step, and so will be affected by the difference in time between recalculations, but I don't think that affect will be bad enough to cause issues... if it is, the recalculation can be off loaded to an ISR, but in no case should it require math more complex than simple addition and subtraction.

Keep in mind, Babbages difference engine design can calculate values for nth order polynomial equations, and it does that all by simply adding and subtracting.
Difference engine - Wikipedia, the free encyclopedia
As long as we take each movement as a separate entity, we can start from a known point and state, then tabulate all the changes to make the move through simple addition and subtraction, /as/ we move, no matter how complex the movement may be. As long as the desired delays between steps can be represented by an nth order poly, we should be able to tabulate them efficiently using only plus and minus: Without multiplication, division, square root, trigonometric or any other more complex calculation..

Far from being insulted, I'm overjoyed that each of these issues is being discussed. The entire point of this is to explore the possibility, and find the problems and (hopefully) solutions for each. The record of these discussions provides others with a quicker path to the goal in the future.

6. @Roman: If you are using the Bresenhams algorithm, there is no need to use floating point as far as I can see. I have detected no error in the routine I have set up so far, and it seems to be doing what it's supposed to be doing. Just a bit confused about your previous comment about what James was doing wrong, because I could not see how any error could sneak in when only using addition and subtraction.

And I know you are a very able person, so I was a bit surprised, and thought that maybe you had never used it (only used your own type or something).

@James: I'm mostly thinking about moving the Z axis and X axis at the same time. On many machines, moving the Z axis can be a lot slower than the X axis. There may be other factors as well (such as different screw types). I'm thinking "worst case scenario" for most of the functions, so that you wont see any odd behavior whatever you do.

Sure I will try to help.

How many timers does it have? And how big?

7. Ok... rethinking the timing here... please change all my mS comments to uS comments in the prior posts? )

So what is a reasonable IPM? I see everything from 5 to 100. People seem to be generally happy with 30 to 60?

60IPM, 10TPI, 600RPM, 10RPS, @6th is 12,000PPS, is 416 instructions per step. I think that is doable... @18th over is 138... doubtful. My guess is I can do around 10,000 steps per second, in C. If I optimized it in asm, I could maybe do 20,000.

Here is a little table I made to compare modes and teeth per inch, and show how many Inches Per Minute that would give you, and how many steps per second would be required.
Code:
IPM	TPI	RPM	RPS	FUL	HALF	4th	6th	8th	16th	18th
60	30	1800	30	6000	12000	24000	36000	48000	96000	108000
60	15	900	15	3000	6000	12000	18000	24000	48000	54000
60	10	600	10	2000	4000	8000	12000	16000	32000	36000
30	30	900	15	3000	6000	12000	18000	24000	48000	54000
30	15	450	7.5	1500	3000	6000	9000	12000	24000	27000
30	10	300	5	1000	2000	4000	6000	8000	16000	18000
10	30	300	5	1000	2000	4000	6000	8000	16000	18000
10	15	150	2.5	500	1000	2000	3000	4000	8000	9000
10	10	100	1.67	333	667	1333	2000	2667	5333	6000
Does that seem reasonable? It seems like it could work for 30IPM, reasonable TPI, and up to 6th overstepping. A good start, if not a top of the line solution.

Or is that not going to be enough? If it isn't possibly useful then I will abandon the current 16F690 processor and put the project on hold until I can sell off the 60 or so PCBs I have (they are very nice for other uses) and design in a faster uC in the next revision. Probably a TI or an Atmel or something... Wish the SX line had grown...

8. James-
I can see how multiple short moves would be very slow... but I don't see how they could possibly stall the steppers... If I had the code space (and I probably won't in the little PIC, but I might in a larger one) I would implement a separate move planner for circles and then process the G codes for circular motion.
I'll reply on this first as I think it is vital. Acceleration will be used to get the motor up to the feed speed (used for cutting) then there will be other small moves all while at the full speed. Imagine cutting an arc as 100 little straight lines. Even if you add some proper g-code circular motion you will still have this issue on slight angles and direction changes etc so you still *always* have to deal with this issue on a CNC cutting machine.

You mentioned using serial, and you are using 32bits per axis coords, so if a serial move command is 13 bytes at 56 kbaud it looks like this;
1. PIC finishes last move
2. PIC sends serial to PC to say move is done (miniumum 1byte); 178 uS
3. PC has to stop picking its nose and respond; ?????? uS
4. PC sends the next 13byte move command; 2314 uS
5. PIC does all move pre-processing; 200? uS
6. PIC finally sends steps to the stepper motor!

So you will get a best-case delay of 2692uS between each short move the stepper motor is making, at full speed which is hopefully 5 revs/second (16000 steps/sec) so the delay with no steps is as bad as a 43 step pause in the middle of the high speed step sequence. This will stall the stepper or maybe cause it to jump steps, AND I have used the very best-case secenario so if you add a Windows delay or slower serial baudrate etc it can be much worse.

The only time you should allow the steps to pause (or any interruption to the stepping) is after the motor has been decelerated to a complete stop. I'll leave the solution up to you.

And the software on the host might be hyperterm (sending simplified G-code processed from real G code by custom software), a custom package, or eventually even a plug in for Mach 3. I'm hoping I can squeeze in enough of a G code interpreter that basic output from design programs could be directly piped over and still work... could be a pipe dream, but it's fun to try, especially with some help.
A nice dream, but will be speed hungry and use a lot of ROM for the g-code interpreter. I would suggest stepping up to a minimum PIC18 at 10 MIPS and 16k ROM.

I'll probably just set the appropriate step bits in that routine, then clear them before the step delay at the end of the main loop needs to be on a timer. It will actually start counting down at the top of the loop, then be adjusted at the end by single path, time consistent code as needed. If the timer has already passed the desired delay... then the unit is working at it's max speed and will limit the motion of the mill.
Both sound like good improvements to add along with the PIC18.

What is a "YZ +1 overrun"? Can that really happen in Bresenham? My understanding is that this was the whole point of the method... it always lands at the exact correct point.
It should. But I don't like to leave anything to chance when it comes to industrial control applications. If y is very close to half of x, and there has been some rounding issue or similar in your pre-processing math then there is a possibilty for Bresenham to overrun, it is one of its weaknesses that only one axis controls duration. If at all possible I would incorporate >0 testing in your step pulse generation so it can never be possible for any axis to generate one pulse too many (or too few).

Your comment about ramp zones completely confuses me. I'm calculating the ramp between every single step, much less every mS.
Yep I saw that. Technically it's superior to adjust the ramp for every step generated (as you are doing), but in real life at full speed there are some issues. At 5 RPS (16000 usteps/rev) this requires you to do 16000 ramp calcs per second, an unneccesary burdon on your already taxed PIC18. Many accel routines (ie Mariss', and my own) change the ramp value every mS, this is plenty fast enough when a typical accel ramp may take a total of 100mS or 200mS etc. Also, if doing linear acceleration (which has a reverse exponential period change) then the later (high speed) stage of the acceleration ramp will barely change its period value over time, so you are paying the cost of doing the calc for every ustep pulse but the period will only change every 50 or 100 pulses or so.

Re: "linear acceleration": Please look at the code again... not only am I changing the period between steps (thereby changing the velocity) I am ALSO changing the /change/ in period (thereby changing the acceleration). Those changes are being recalculated with each step, and so will be affected by the difference in time between recalculations, but I don't think that affect will be bad enough to cause issues... if it is, the recalculation can be off loaded to an ISR, but in no case should it require math more complex than simple addition and subtraction.
Sorry James! I'd appreciate if you can walk me though the math there, that wasn't immediately obvious although I confess I was mainly just throwing your code into the compiler to count cycles and see how slow it was going to be.

If there is an easy fast way to generate a proper linear accel ramp without big divisions or mults I would like to see it. If you are reducing the *amount* every step that you subtract from the period you still need to reduce that amount exponentially. What I suggest is re-writing your ramp system to use a standard 1mS ramp "zone" and then you only have one exponential component to deal with, not two.

Otherwise if you keep doing the ramp per-step then you have 2 issues;
1. compensation for the double exponential.
2. how to get enough resolution for the high speed end of the ramp where you only change period every 100 steps.

Seriously I think you are running out of MIPS so quickly trying to do this real time high speed math it's probably most sane to move it to a PIC14 "dsp" microcontroller that is designed for this type of thing.

60IPM, 10TPI, 600RPM, 10RPS, @6th is 12,000PPS, is 416 instructions per step. I think that is doable...
I think that may be possible with smart hand-tuned assembler on a 10 MIPs PIC18. But even then, 12000 PPS is considered very slow by many of the users on this forum and limits your market to only some low performance oriented people.

TheBlight-
If you are using the Bresenhams algorithm, there is no need to use floating point as far as I can see. ...
Correct! I posted that without seeing which system James was using for XY interpolation.

But it's still six of one and half a dozen of the other. To get the fractional interpolation you either need to add/subtract a fraction (float) number of sufficient resolution, or use a large high resolution integer ie; a worst-case Bresenham that uses full sized 32bit integers and no Bresenham optimisation tricks (like James' current version). Both float and worst-case Bresenham systems carry the penalty of excess resolution.

9. On the PC to uC communications:

I don't see any reason for the uC to reply to the PC, unless there is an error or a full buffer. And the USB version of the RLC has a 640 byte transmit buffer, so data should always be waiting for the uC... Also, G code is simple enough that I'm pretty sure I can interpret it as it comes in, byte at a time, right in the ISR and so always have a point ready to go. I've done much more complex interpreting on the fly in an MSP430 high speed logger for testing military rockets in flight.

The interpreter is just a state machine, so for any one incoming byte, it's just a calculated jump, then compare the new byte with one of a few possible values, set the new state, and if it's a numerical digit, a -='0', coordinate * 10 and add.

Times ten is just "DST = ((SRC << 2) + SRC) << 1;" which a good compiler will assemble as less than 30 instructions for a 16 bit value and less than 50 for a 32 bit value:
Code Generator Results

Even if I can't interpret it on the fly, I can buffer the data so it doesn't have to wait for the PC to send it. The USB has a huge buffer, and the PIC should be able to buffer 20 or 30 characters, which is more than the average line of G code.

Yes, that does require hardware handshaking, but it is supported on the RLC1 and it doesn't take but a couple of cycles to send an XOFF back to the RLC3.

On ramping:

If you are reducing the *amount* every step that you subtract from the period you still need to reduce that amount exponentially.
Babbage difference engine. You can do things exponentially by changing the amount that you change the amount that you change. Nothing but addition and subtraction in that.

Code:
#define		sv	STARTING_VELOCITY
//starting velocity as uSecond delay time between steps.

short		mv = MAXIMUM_VELOCITY;
//maximum velocity as uSecond delay time between steps.
//e.g. the value we change

short		ma = MAXIMUM_DELTA_ACCELERATION;
//maximum acceleration
// the change in the value we change

#define 		mda	MAXIMUM_DELTA_ACCELERATION
//maximum change in acceleration
// the change in the change in the value we change

//steps remaining
tcoordinate sr;

//stopping distance. This keeps track of how long we expect to take to get stopped
//once we get going.
tcoordinate	sd = 0;	//stopping distance
Code:
//figure out how long to wait for the next step by considering position, velocity, and acceleration
if ( sr > 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.
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
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.
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
} // end if ( i > sd )
So the ( sr > sd ) splits the movement into the acceleration phase and the deceleration phase. We want to go faster if it's true, and slower if it's false (the code in the matching else).

The ( xv > mv ) splits the acceleration phase into actual acceleration and running at speed.

The (xv >= (mv+((sv-mv)>>1))) splits the actual acceleration into ramping acceleration UP and ramping it back down.

Notice that the velocity (xv) is increased or decreased by ma, but that ma is also increased or decreased by mda.

If we needed to do so, we could make mda a short, and increase and decrease it as well...

Nothing but addition and subtraction, but the effect is of an exponential change.

Yeah? No?

On the max PPS:

I'm not trying to make a professional system on the first shot here... I'd like to make something open source, hobby level, experimental, just to play with. If there is an interest, I will revise the current board and apply a much faster processor. As long as it will pulse fast enough, in a minimal microstepping mode, on a low TPI thread, or just slowly, and demonstrate that it can do something minimally useful, I'll keep working on it.

Honestly, I think it's probably going to end up as sample code for the Arduino or something, but the existing board plugs /right/ into the Arduino (as an Arduino "Shield") and so that still does me good.

Mostly I just like to see what can be done.

10. Originally Posted by James Newton
...
The USB has a huge buffer, and the PIC should be able to buffer 20 or 30 characters, which is more than the average line of G code.

Yes, that does require hardware handshaking, but it is supported on the RLC1 and it doesn't take but a couple of cycles to send an XOFF back to the RLC3.
Using a USB serial chip can save you there, as you say. Many of the g-code programs I've seen have thousands of move commands so there has to be some system of handshake back to the PC. If the USB chip can do it automatically that is a good solution.

Just keep in mind the pre-processing time, which will be significant on a PC16! As an example; the g-code will specify a feed speed, this is always a hypotenuse speed. Then your PIC will receive the move command as XYZ so it will need to calculate the hypotenuse distance as hdist = sqroot(x sq + y sq + z sq) which is going to be pretty nasty in 32bit math on a PIC16. That alone will cost you 300 to 400 ROM for the math routines and maybe 1000 to 2000 instructions in processing time.

Once it has the hypotenuse distance the longest axis speed must be scaled as; xspeed = (hspeed * xdist) / hdist, (a serious calc in 32bit) then (fortunately) due to using Bresenham you can automatically derive yspeed and zspeed from xspeed.

There is actually another nasty scaling calc, as the g-code will specify speed in IPM and the PIC needs to know the steps/inch of the main axis, then convert the requested IPM into steps/second (or more accurately a step period), that will probably be 32bit math like this; step_period = (IPM * scaling_factor) / binary_constant.

You can't afford to have anything delay generation of a step so all your preprocessing has to be done simultaneously with (and subservient to) step generation. Something to think about.

Originally Posted by James Newton
On ramping:
Babbage difference engine. You can do things exponentially by changing the amount that you change the amount that you change. Nothing but addition and subtraction in that.
I got the principle, its the implementation that bothers me. Especially if you want to run it in reverse and get back to where you started (ie; balanced accel/decel).

To get linear accel with a per-mS calc (as I used) the range of the value subtracted from period is quite large. Here are some values I generated for my linear accel table at 0.3 G over 126 mS (126 samples);

Code:
// first 10 values of the 126 values in the ramp
13245,     //start speed
8830,
6623,
5298,
4415,
3784,
3311,
2943,
2649,
2408,

279,	// last 12 values of the 126 values in the ramp
276,
273,
260,
270,
268,
258,
265,
262,
260,
257,
255
The values are in 0.1uS resolution, so the very last period is 25.5uS period to generate each step pulse. See the last mS of the ramp, over that mS (39 step pulses) the exponential is so gentle the period has only changed less than 1% (or 0.2uS) change in period over 39 steps.

Your case is much worse than this, as you are not ramping over time but per step, so you factor the exponential twice it's actually the square of the exponential! For your system your would be subtracting 0.02% from the period each step by the end of the ramp, which is -0.0025uS!

If you get the time, whip up C code or an excel spreadsheet of your double exponential generating all the steps from period 1324.5 uS to period 25.6 uS and have a look at what the period value will actually be doing, the last stage will be nasty and you will need some high res value for the subtractor to get the top of the ramp ok and especially if you want to be able to reverse it.

Originally Posted by James Newton
(re ramp)
Notice that the velocity (xv) is increased or decreased by ma, but that ma is also increased or decreased by mda.
See my figures above, at the first step of the ramp you will be subtracting 445.1uS per step, at the end of the 126mS ramp you will be subtracting 0.0025uS per step.

Originally Posted by James Newton
I've seen 2 documentaries on it (I'm addicted to the history and science channels) including that beautiful working model they built for the London museum. It's the size of a large car!

Originally Posted by James Newton
On the max PPS:

I'm not trying to make a professional system on the first shot here... I'd like to make something open source, hobby level, experimental, just to play with. If there is an interest, I will revise the current board and apply a much faster processor. As long as it will pulse fast enough, in a minimal microstepping mode, on a low TPI thread, or just slowly, and demonstrate that it can do something minimally useful, I'll keep working on it.
...
That will make life a lot easier, if you stick to quite low step rates and find a way to fudge some of the math (see hypotenuse math above). It's still ambitious to generate these step pulses and accel/decel ramps on a PIC16 and (IMHO) practically impossible to real-time interpret g-code at the same time on a PIC16...

11. I sat down after dinner and whipped up an Excel sheet for linear accel at 0.3 G (same as my examples above) but this time the exact linear accel is calculated per step (not per 1mS "zone").

Like my example above it starts at 6mm/sec and finishes around 300mm/sec, and takes about 101mS. It takes 2009 steps.

Here's the first 60 steps;
Code:
Test of linear acceleration 0.3 G calculated for every step pulse...

Steps  StepPeriod(uS)  ElapsedTime(uS)  Speed(mm/Sec)
0	6.00
1	1322.9	1322.9	9.97
2	796.2	2119.2	12.36
3	642.3	2761.5	14.28
4	555.7	3317.2	15.95
5	497.6	3814.8	17.44
6	455.0	4269.8	18.81
7	422.0	4691.8	20.08
8	395.4	5087.2	21.26
9	373.3	5460.5	22.38
10	354.6	5815.1	23.45
11	338.6	6153.7	24.46
12	324.5	6478.2	25.43
13	312.1	6790.3	26.37
14	301.0	7091.3	27.27
15	291.0	7382.3	28.15
16	282.0	7664.3	28.99
17	273.8	7938.1	29.81
18	266.2	8204.3	30.61
19	259.3	8463.6	31.39
20	252.9	8716.4	32.15
21	246.9	8963.3	32.89
22	241.3	9204.7	33.61
23	236.1	9440.8	34.32
24	231.3	9672.1	35.02
25	226.7	9898.8	35.70
26	222.4	10121.1	36.36
27	218.3	10339.4	37.02
28	214.4	10553.8	37.66
29	210.8	10764.6	38.29
30	207.3	10971.9	38.92
31	204.0	11175.8	39.53
32	200.8	11376.6	40.13
33	197.8	11574.4	40.72
34	194.9	11769.3	41.31
35	192.2	11961.5	41.88
36	189.5	12151.0	42.45
37	187.0	12338.0	43.01
38	184.5	12522.5	43.57
39	182.2	12704.7	44.11
40	179.9	12884.6	44.65
41	177.8	13062.4	45.19
42	175.7	13238.0	45.71
43	173.6	13411.7	46.24
44	171.7	13583.4	46.75
45	169.8	13753.1	47.26
46	168.0	13921.1	47.76
47	166.2	14087.3	48.26
48	164.5	14251.7	48.76
49	162.8	14414.6	49.24
50	161.2	14575.7	49.73
51	159.6	14735.4	50.21
52	158.1	14893.5	50.68
53	156.6	15050.1	51.15
54	155.2	15205.3	51.62
55	153.8	15359.0	52.08
56	152.4	15511.5	52.53
57	151.1	15662.5	52.99
58	149.8	15812.3	53.44
59	148.5	15960.9	53.88
60	147.3	16108.2	54.32
And here are the last 30 steps of the 2009 steps;
Code:
1979	25.8	100407.1	307.22
1980	25.8	100432.9	307.30
1981	25.8	100458.7	307.38
1982	25.8	100484.5	307.45
1983	25.8	100510.4	307.53
1984	25.8	100536.2	307.61
1985	25.8	100562.0	307.69
1986	25.8	100587.8	307.76
1987	25.8	100613.6	307.84
1988	25.8	100639.3	307.92
1989	25.8	100665.1	308.00
1990	25.8	100690.9	308.07
1991	25.8	100716.7	308.15
1992	25.8	100742.4	308.23
1993	25.8	100768.2	308.30
1994	25.7	100793.9	308.38
1995	25.7	100819.7	308.46
1996	25.7	100845.4	308.54
1997	25.7	100871.1	308.61
1998	25.7	100896.8	308.69
1999	25.7	100922.5	308.77
2000	25.7	100948.3	308.84
2001	25.7	100974.0	308.92
2002	25.7	100999.7	309.00
2003	25.7	101025.3	309.08
2004	25.7	101051.0	309.15
2005	25.7	101076.7	309.23
2006	25.7	101102.4	309.31
2007	25.7	101128.0	309.38
2008	25.7	101153.7	309.46
2009	25.6	101179.3	309.54

(edit) Sorry I couldn't get the 4 columns to line up with the 4 titles, Excel is poor at exporting text.

12. Originally Posted by RomanLini
Just keep in mind the pre-processing time, which will be significant on a PC16! As an example; the g-code will specify a feed speed, this is always a hypotenuse speed. Then your PIC will receive the move command as XYZ so it will need to calculate the hypotenuse distance as hdist = sqroot(x sq + y sq + z sq) which is going to be pretty nasty in 32bit math on a PIC16. That alone will cost you 300 to 400 ROM for the math routines and maybe 1000 to 2000 instructions in processing time.

Once it has the hypotenuse distance the longest axis speed must be scaled as; xspeed = (hspeed * xdist) / hdist, (a serious calc in 32bit) then (fortunately) due to using Bresenham you can automatically derive yspeed and zspeed from xspeed.

There is actually another nasty scaling calc, as the g-code will specify speed in IPM and the PIC needs to know the steps/inch of the main axis, then convert the requested IPM into steps/second (or more accurately a step period), that will probably be 32bit math like this; step_period = (IPM * scaling_factor) / binary_constant.

You can't afford to have anything delay generation of a step so all your preprocessing has to be done simultaneously with (and subservient to) step generation. Something to think about.
Yeah, ok, that isn't happening in a PIC, and I don't see a way around it. Ok, g-code is out the window. We are going to use a proprietary format which specifies how many steps to take on each axis, and the maximum acceleration and velocity to move the "big" axis (the one with the most steps).

Originally Posted by RomanLini
I got the principle, its the implementation that bothers me. Especially if you want to run it in reverse and get back to where you started (ie; balanced accel/decel).

To get linear accel with a per-mS calc (as I used) the range of the value subtracted from period is quite large. Here are some values I generated for my linear accel table at 0.3 G over 126 mS (126 samples);

<SNIP>

The values are in 0.1uS resolution, so the very last period is 25.5uS period to generate each step pulse. See the last mS of the ramp, over that mS (39 step pulses) the exponential is so gentle the period has only changed less than 1% (or 0.2uS) change in period over 39 steps.

Your case is much worse than this, as you are not ramping over time but per step, so you factor the exponential twice it's actually the square of the exponential! For your system your would be subtracting 0.02% from the period each step by the end of the ramp, which is -0.0025uS!

If you get the time, whip up C code or an excel spreadsheet of your double exponential generating all the steps from period 1324.5 uS to period 25.6 uS and have a look at what the period value will actually be doing, the last stage will be nasty and you will need some high res value for the subtractor to get the top of the ramp ok and especially if you want to be able to reverse it.

See my figures above, at the first step of the ramp you will be subtracting 445.1uS per step, at the end of the 126mS ramp you will be subtracting 0.0025uS per step.
I've been staring at this for a while, and I don't doubt you know what you're talking about, but I'll be darned if I can understand why the step rate changes so much at the start, and so little at the end of the ramp...

Lets try a thought experiment and maybe it will help me understand: Lets say you have a motor that is already running at 1 step per 500uS. If you change the step rate to 1 per 250uS, it's running twice as fast, right? And that was a change of 250uS per step. If you change it to 1000uS (1mS) it's running half as fast. But that was a change of 500uS per step. Is that where the difference comes in? As your time between steps is decreased, the same change in step delay is a greater percentage of the total delay, and so has a greater effect on the speed of the motor. And that assumes you change the step delay ever constant time interval.. if you change it every step, then the problem is worse because the same change is being made more often when it also has a great effect; when the step delay is slower...

Ok... I think I see.

But that just means the equation to calculate the change in step delay is more complex, not that it can't be calculated by the Babbage method.

What is the actual formula you used to calculate those step delays in excel? Can you share that?

Originally Posted by RomanLini
I've seen 2 documentaries on it (I'm addicted to the history and science channels) including that beautiful working model they built for the London museum. It's the size of a large car!
They have a second one, built by the same people, in Mountain View, here in California... 8 hours drive from me. I'm hoping to go some day and maybe even get to turn the crank!
The Babbage Engine | Computer History Museum

Page 4 of 12 First 1234567 ... Last

1. ###### Building a Cheap DIY CNC Machine (1st attempt) - FoxyTronics
06-08-2013, 09:50 AM
2. ###### How to generate a PWM with varying frequency?
03-15-2013, 10:32 AM
3. ###### Generating a linear delay from min to max.
05-21-2012, 10:22 PM
4. ###### Generating a linear delay from min to max.
05-10-2012, 11:21 AM