3D Printer, 3D Bresenham's Line in SPIN.
Here:
Note that CX, CY, and CZ are the current X, Y, and Z positions.
VAR
LONG CX
LONG CY
LONG CZ
LONG DX
LONG DY
LONG DZ
LONG SX
LONG SY
LONG SZ
LONG ER
LONG E
LONG E2
LONG ER2
PUB LineTo3D(X,Y,Z)
DX := UABS(CX - X)
IF CX < X
SX := 1
ELSE
SX := -1
DY := UABS(CY - Y)
IF CY < Y
SY := 1
ELSE
SY := -1
DZ := UABS(CZ - Z)
IF CZ < Z
SZ := 1
ELSE
SZ := -1
IF DX > DY
ER := DX / 2
IF DX > DZ
ER2 := DX / 2
ELSE
ER2 := -DZ / 2
ELSE
IF DY > DZ
ER2 := DY / 2
ELSE
ER2 := -DZ / 2
ER := -DY / 2
REPEAT WHILE (TRUE)
IF X == CX AND Y == CY
QUIT
E := ER
E2 := ER2
IF E > (-DX)
ER -= DY
CX += SX
MvX(SX)
IF E < DY
ER += DX
CY += DY
MvY(SY)
IF DX > DY
IF E2 > (-DX)
ER2 -= DZ
IF E2 < DZ
ER2 += DX
CZ += SZ
MvZ(SZ)
ELSE
IF E2 > (-DY)
ER2 -= DZ
IF E2 < DZ
ER2 += DY
CZ += SZ
MvZ(SZ)
And the 2D version from which the 3D was derived (also mine):
VAR
LONG CX
LONG CY
LONG DX
LONG DY
LONG SX
LONG SY
LONG ER
LONG E
PUB LineTo(X,Y)
DX := UABS(CX - X)
IF CX < X
SX := 1
ELSE
SX := -1
DY := UABS(CY - Y)
IF CY < Y
SY := 1
ELSE
SY := -1
IF DX > DY
ER := DX / 2
ELSE
ER := -DY / 2
REPEAT WHILE (TRUE)
IF X == CX AND Y == CY
QUIT
E := ER
IF E > (-DX)
ER -= DY
CX += SX
MvX(SX)
IF E < DY
ER += DX
CY += DY
MvY(SY)
Note that CX, CY, and CZ are the current X, Y, and Z positions.

Comments
x, y, z variables by steps of YZ, ZX, XY (where upper case X, Y, Z are the total steps in each axis), until
they all reach XYZ - whichever of x, y, z is smallest produces a step, then gets incremented, and the time
value is bressenham'd against this smallest value. Time goes T ticks as the x, y, z values go XYZ. Typically
64 bit arithmetic is needed for this.
This doesn't help with acceleration / deceleration ramps though, but that can be added as a more complex
bressenham style relationship between T and x,y,z I think.
[ by the way you have explicitly ignore axes with zero movement, otherwise the XYZ product falls to zero ]
Assuming the XYZ motors can start and accellerate at the same speed the longest line would determine the maximum starting speed and acceleration. The other axes would have have longer delta times based on it's length relative to that of the longest line. The rates of accelleration could also be calculated the same way.
Assuming that all steppers are of equal ability:
PSEUDOCODE: That is just off the top of my head, then have a separate loop watching the counter for when to step each. Is that the basic idea of what you are suggesting??
BTW, I'd forget about Bresenham. It's not suitable for motor control due to the jitter. The approach cited by Mark_T is much better.
-Phil
I have recently done something similar with my drivers, but they all run in different cogs and the speed is constant with no ramping. The longest axis is first determined and the total travel time is calculated. The other axes step pulse rate are determined by the total travel time of the longest axis.
Though I do thank you for your thoughts,.
Close, but not exactly what I had in mind. Sometimes I have a hard time putting what I visualize into words so I will try a simple example using motor steps instead of actual distances. No accelleration and maximum speed is 1000 steps/second for X, Y, and Z.
Lets say we want to move from X = 1, Y = 1, Z = 1 to X = 1001, Y = 501, Z = 251
The number of motor steps would be Xs = 1000, Ys = 500, Zs = 250
The delta time (in clks at 80MHz) would be Xd = 80_000, Yd = 160_000, Zd = 320_000
If each stepper is controlled by it's own cog and the 3 stepper cogs are synchronized all they need to “draw” or travel over that line is the number of steps to take and the delta time between each step.
Cog X would step the motor 1000 times with a delay of 80,000 clocks between each step.
Cog Y would step the motor 500 times with a delay of 160,000 clocks between each step.
Cog Z would step the motor 250 times with a delay of 320,000 clocks between each step.
Not much more difficult to have a pasm program running in a single cog do it either.
So Assuming a move from X,Y,Z to X1,Y1,Z1; if Cog X is the greatest distance, and X is to move with a delay of XDelay between stepps, then the speed for the other two would be:
YDelay := XDelay * (abs(x1-x) / abs(y1-y))
ZDelay := XDelay * (abs(x1-x) / abs(y1-y))
Is that what you are saying?
If I understand correctly I think I get the idea. I think it is time for me to write a little PASM (thankfully my preferred language on the propeller).
I give you many thank you's for the advice.
You mean something like:
'This is a simple stepper motor controler for a three axis device like a 3D printer. 'We are only using full stepping for this one. CON xBase = 0 yBase = 4 zBase = 8 xPinM = $000F 'Pin Mask for X axis stepper. yPinM = $00F0 'Pin Mask for Y axis stepper. zPinM = $0F00 'Pin Mask for Z axis stepper. zrPin = 12 'Zeroing switch input. xMax = $7FFFFFFF 'Furthest from zero allowed for X axis. yMax = $7FFFFFFF 'Furthest from zero allowed for Y axis. zMax = $7FFFFFFF 'Furthest from zero allowed for Z axis. xDely = 156250 yDely = 156250 zDely = 156250 'The parameter offsets in the array that the cog reads: pCX = 0 pCY = 1 pCZ = 2 pNX = 3 pNY = 4 pNZ = 5 pDX = 6 pDY = 7 pDZ = 8 pGo = 9 VAR LONG DY, DX, DZ LONG PBLCK[12] LONG xDelay, yDelay, zDelay PUB MoveTo(ax, ay, az) DX := UABS(ax - PBLCK[pCX]) DY := UABS(ay - PBLCK[pCY]) DZ := UABS(az - PBLCK[pCZ]) IF DX > DY AND DX > DZ PBLCK[pDX] := xDelay PBLCK[pDy] := ((DX << 4) * ((DX / DY) << 4)) >> 4 PBLCK[pDz] := ((DX << 4) * ((DX / DZ) << 4)) >> 4 pBLCK[pNX] := ax pBLCK[pNY] := aY pBLCK[pNZ] := aZ PBLCK[pGo] := 1 PRI UABS(v) IF v < 0 v := -v RETURN v PUB Init COGNEW(Main, @PBLCK) xDelay := xDely yDelay := yDely zDelay := zDely DAT ORG Main MOV DIRA, Msk :Lp JMPRET GetP_R,GetP OR CG, CG WZ IF_NZ JMPRET DoLine_R, DoLine IF_Z JMP #:Lp '┌──────────────────────────────────────────────────────────────────┐ '│PROC: GetP │ '│ │ '│*DESCRIPTION: │ '│ Reads the parameter block from HUB RAM. Takes 432 to 446 clock │ '│ cycles. │ '│ │ '└──────────────────────────────────────────────────────────────────┘ GetP MOV Ptr, PAR 'PAR points at the Parameter block. MOV Lptr, #CurX 'COG Address of first parameter. MOV C, #12 Lp0 MOVS upd0, ptr ADD LPtr, #4 upd0 RDLONG Lptr, Ptr DJNZ C, #Lp0 JMP GetP_R PutP MOVI upd0, :upw JMP GetP '142, 18 :upw WRLONG Lptr, Ptr PutP_R GetP_R RET '┌──────────────────────────────────────────────────────────────────┐ '│PROC: PutP │ '└──────────────────────────────────────────────────────────────────┘ 'PutP MOV Ptr, PAR 'PAR points at the Parameter block. ' MOV Lptr, #CurX 'COG Address of first parameter. ' MOV C, #12 ':Lp MOVS :upd, ptr ' ADD LPtr, #4 ':upd WRLONG Lptr, Ptr ' DJNZ C, #:Lp 'PutP_R RET '┌──────────────────────────────────────────────────────────────────┐ '│PROC: DoLine: │ '│ │ '│*DESCRIPTION: │ '│Moves X, Y, and Z based on the delays until the move is complete. │ '│ │ '│ │ '└──────────────────────────────────────────────────────────────────┘ DoLine MOV XT, CNT MOV YT, CNT MOV ZT, CNT ADD XT, XDly ADD YT, YDly ADD YT, YDly :Lp CMP XT, CNT WZ,WC IF_BE ADD XT, CNT IF_BE JMPRET MvX_R, MvX CMP YT, CNT WZ,WC IF_BE ADD YT, CNT IF_BE JMPRET MvY_R, MvY CMP YT, CNT WZ,WC IF_BE ADD YT, CNT IF_BE JMPRET MvY_R, MvY CMP CurX, X WZ,WC IF_BE CMP CurY, Y WZ,WC IF_BE CMP CurZ, Z IF_BE JMPRET PutP_R,PutP IF_BE RET JMP #:LP DoLine_R RET '┌──────────────────────────────────────────────────────────────────┐ '│PROC: MvX: │ '│ │ '└──────────────────────────────────────────────────────────────────┘ MvX SUB X, CurX NR,WC,WZ MOV T, Stps IF_Z JMP MvX_R IF_C SUB CurX, #1 IF_NC ADD CurX, #1 MOV T0, CurX AND T0, #$3 SHL T, XBase SHR T, T0 MOV T0, OUTA ANDN T0, XMsk JMP #Mv_T '┌──────────────────────────────────────────────────────────────────┐ '│PROC: MvY: │ '│ │ '└──────────────────────────────────────────────────────────────────┘ MvY SUB Y, CurY NR,WC,WZ MOV T, Stps IF_Z JMP MvX_R IF_C SUB CurY, #1 IF_NC ADD CurY, #1 MOV T0, CurY AND T0, #$3 SHL T, YBase SHR T, T0 MOV T0, OUTA ANDN T0, YMsk JMP #Mv_T '┌──────────────────────────────────────────────────────────────────┐ '│PROC: MvZ: │ '│ │ '└──────────────────────────────────────────────────────────────────┘ MvZ SUB Z, CurZ NR,WC,WZ MOV T, Stps IF_Z JMP MvX_R IF_C SUB CurZ, #1 IF_NC ADD CurZ, #1 MOV T0, CurZ AND T0, #$3 SHL T, ZBase SHR T, T0 MOV T0, OUTA ANDN T0, ZMsk Mv_T OR T0, T MOV OUTA, T0 MvX_R MvY_R MvZ_R RET CurX LONG 0 'The current X Position in steps. CurY LONG 0 'The current Y Position in steps. CurZ LONG 0 'The current Z Position in steps. X LONG 0 'The requested X position in steps. Y LONG 0 'The requested Y position in steps. Z LONG 0 'The requested Z position in steps. XDly LONG xDely 'The delay between X steps, in clock cycles. YDLY LONG yDely 'The delay between Y steps, in clock cycles. ZDLY LONG zDely 'The delay between Z steps, in clock cycles. CG LONG 0 'A flag to tell us when to go. C LONG 0 'Used in loops as an index. T LONG 0 'Used to store in process values. T0 LONG 0 XT LONG 0 YT LONG 0 ZT LONG 0 Stps LONG %0011_0110_1100_1001 'The step output values. Msk LONG (xPinM | yPinM | zPinM) XMsk LONG xPinM YMsk LONG yPinM ZMsk LONG zPinM Lptr LONG 0 Ptr LONG 0 'The states of a the four pins to control a steper in forward order. 'cStep BYTE %0011, %0110, %1100, %1001 {{ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ TERMS OF USE: MIT License │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ │files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ │modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ │is furnished to do so, subject to the following conditions: │ │ │ │The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ │ │ │THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ │WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ │COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ │ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }}Yes, that looks pretty much like what I had in mind. I have only taken the time to skim through your code, so I may be wrong.