Code archives/User Input/Stepper Motor Control

This code has been declared by its author to be Public Domain code.

Download source code

Stepper Motor Control by jfk EO-111102007
Get the sourcecode together with the required DLPORTIO.DLL and dlportio.decls from here:

http://www.melog.ch/cnc/smc800_io.zip
; ****************************************************************************************************
; ********************* CALLING THE SMC800 STEPPERMOTOR CONTROLLER FROM BLITZ3D **********************
; ****************************************************************************************************

; This Program shows how to call the SMC800 Steppermotor Controller Card trough the Parallel Port.
; Other Controllers of the SMC Series may work with it too (SMC1500 etc.). See www.emis.com.

; The SMC can control 3 Stepper Motors and one contact switch. The contact switch is used
; to self-calibrate the Machine. Each axis can travel to its Zero Point, defined by a contact switch.
; After reaching the contact switch it has to move back a certain amount of mm (again out of the contact
; zone), so it's possible to start each row of a CNC Raster Milling Process at pretty much
; the same loaction. The 3 Switches are simply wired parallel and connected to the SMC800.
; This allows to "home" each Motor individually. Warning: using the Function "MotorHome()" 
; without working contact switches may cause your motors to seek for contact 
; forever. 

; Unlike many other Control Cards, the SMC800 expects a rudimentary Amplitude Sinus Modulation for the
; two phases of the Steppermotors. It's really a good thing to study and understand stepper Motors.
; The card offers 800mA for each Phase only, the SMC1500 allows 1.5Amp. Motors must be Two Phase
; Stepper Motors, operating at the same voltage that the SMC's power supply provides, this may be
; 12 To 30 Volts.

; The phase modulation can have a frequency up to 5 kHz. Due to the lack of programmable timers
; in Blitz3D we will use a Polling system here, based on the 1 kHz System Timer.


; The Comments in the Program explain the basic concept of calling the card in order to run
; a 3 Axis CNC Router/Locator. The following XYZ Convention is used:

; X = Motor 0 (left / right)
; Y = Motor 1 (down / up)
; Z = Motor 2 (back / forward)

; CNC People often name the vertical axis "Z", which is wrong from a scientific point of view. 
; In this Demo "Y" is the vertical axis. You may alter this in the code if you prefere Z for the
; vertical axis.



; In Addition to Blitz3D, a DLL is used, "DLPORTIO.dll" and also the blitz userlib "dlportio.decls",
; containing the following declarations:

;.lib "dlportio.dll"

;DlPortReadPortUchar%( port% )
;DlPortReadPortUshort%( port% )
;DlPortReadPortUlong%( port% )

;DlPortReadPortBufferUchar( port%, buffer*, count% )
;DlPortReadPortBufferUshort( port%, buffer*, count% )
;DlPortReadPortBufferUlong( port%, buffer*, count% )

;DlPortWritePortUchar( port%, value% )
;DlPortWritePortUshort( port%, value% )
;DlPortWritePortUlong( port%, value% )

;DlPortWritePortBufferUchar( port%, buffer*, count% )
;DlPortWritePortBufferUshort( port%, buffer*, count% )
;DlPortWritePortBufferUlong( port%, buffer*, count% )

; Special Thanks to the author of PortIO.dll for BLitz3D, right now I cannot find his name, not even
; in the original portio.zip, so please excuse. 



; This Demo Program is not a functional CNC Tool, it only shows how to call the
; Stepper Motors. Implementing a simple CNC Mill is pretty straight forward when
; you consider X and Z as the working space, and Y as the heights taken from a heightmap,
; pixel by pixel, or from a 3D scene using LinePick or CameraPick on a certain field of view.

; You may however add usage of Start- and Stop-Speed for the stepper motors, because this
; will allow to operate a machine with less vibration and less material stress, including 
; stress for the motors. There are several books and Websites about CNC programming.


Graphics 800,600,32,2 ; set up graphics
SetBuffer BackBuffer()



Global max_motor=2  ;(0=x, 1=y, 2=z)


Dim invert_axis(max_motor) ; here you can easily reverse a motors logic (0=normal operation, 1=reverse operation)
invert_axis(0)=1
invert_axis(1)=0
invert_axis(2)=0
;(these are the proxxon mf70 cnc settings)


Global LPT=$378 ; this is the parallel port of your machine. The number may differ, see "Resources" in the device manager's parallel port settings.
                ; and/or Bios. Note: you may have to disable the "bidirectional" parallel port mode in the bios by choosing eg. "unidirectional".


Dim phase_A(max_motor,8)    ; In these arrays we'll store the amplitude modulation curves
Dim phase_B(max_motor,8)
Dim phase_count#(max_motor) ; Used as modulation curves index





; The SMC800 allows for 3 levels of power: 20%, 50% and 100%
; In fact this will only set the curves to something more flat.
SetMotorPower(0,100) ; use 20, 50 or 100
SetMotorPower(1,100)
SetMotorPower(2,100)









Global Special_msg$="" ; used to tell the user some infos







; Defining some standard speeds, where only the Reference Travel speed is used in this example.
cnc_MainSpeed#=0.25
cnc_StartSpeed#=0.125
cnc_StopSpeed#=0.125
cnc_RefSpeed#=0.5
; Note. these speeds represent the frequency in kHz. 1.0 is the max! Use a fraction of 1.0 (1.0, 0.5, 0.25, 0.2,  0.125 ...)






;************************************     Main Test Loop     ***************************************
While KeyDown(1)=0
 Cls
 Text 0,0, "Cursor= left right, back , forward"
 Text 0,16,"PgUp/Dn= up, down"
 Text 0,32,"H= Home all Motors"
 Text 0,48,"Esc= Exit"
 Text 0,64, Special_msg$ ;  ; (Number of pulses for last MotorHome(): always Z axis in this demo, because it was the last one)
 Flip

 ; manual control of the 3 Motors
 k=203
 If KeyDown(k)
  While KeyDown(k)
   sync_timer(cnc_RefSpeed#)
   status = call_motor(0, 1) ; x minus
  Wend
 EndIf
 k=205
 If KeyDown(k)
  While KeyDown(k)
   sync_timer(cnc_RefSpeed#)
   status = call_motor(0, 0) ; x plus
  Wend
 EndIf
 k=208
 If KeyDown(k)
  While KeyDown(k)
   sync_timer(cnc_RefSpeed#)
   status = call_motor(2, 1) ; z minus
  Wend
 EndIf
 k=200
 If KeyDown(k)
  While KeyDown(k)
   sync_timer(cnc_RefSpeed#)
   status = call_motor(2, 0) ; z plus
  Wend
 EndIf
 k=209
 If KeyDown(k)
  While KeyDown(k)
   sync_timer(cnc_RefSpeed#)
   status = call_motor(1, 1) ; y minus
  Wend
 EndIf
 k=201
 If KeyDown(k)
  While KeyDown(k)
   sync_timer(cnc_RefSpeed#)
   status = call_motor(1, 0) ; y plus
  Wend
 EndIf

 ; home...
 k=35
 If KeyHit(35) Then
  ; (Note: it's neccessary to first Home the vertical motor!)
  MotorHome(1,cnc_RefSpeed#)
  MotorHome(0,cnc_RefSpeed#)
  MotorHome(2,cnc_RefSpeed#)
 EndIf

Wend
End














Function call_motor(motor, direction)

 ; motor choice: 0,1,2
 ; direction: 0 (away from home) or 1 (back to home)


 STB = LPT + 2 ; parallel port status port
 stb_byte = inp(STB) ; (we need this later for a printerport handshake)

 ;*** assembling the data byte for the SMC ***
 pc=Floor(phase_count(motor))
 byte=(phase_A(motor,pc) Or phase_B(motor,pc))  Or (motor Shl 6)


 ;*** Sending Data to Controller ***
 outp(LPT, (byte))   ; Actually send the Amplitude data and Motor Selection


 ; *** LPT Handshake *** 
 ;The following low level handshake may be a relict of needle printer times, 
 ;but it seems it's still neccessary with a lot of of parallel port devices, 
 ;such as this control card.
 outp(STB, stb_byte Or 1) ; gotta do a quick handshake to the "printer"
 ; eventually have to add a small delay here: (tho seems to work without it)
 ; For i=0 To 1000
 ; Next
 outp(STB, stb_byte And (~1)) ; note: "~1" means "Not(1)", or simply "254"
 ; end of LPT handshake


 ; Increment/Loop Phase curve index
 If direction=invert_axis(motor)
  phase_count(motor)=phase_count(motor)+1.0
  If phase_count(motor)>=8.0 Then phase_count(motor)=phase_count(motor)-8.0
 Else
  phase_count(motor)=phase_count(motor)-1.0
  If phase_count(motor)<0.0 Then phase_count(motor)=phase_count(motor)+8.0
 EndIf



 ; *** Checking the Contact Switch ***
 ; At the IO Port LPT+1 we read the control byte of the parallel port. Bit number 8
 ; will signal if the contact switch is closed or not:
 ; Bit is set: no contact
 ; Bit is zero: contact!

 CTR = LPT + 1 ; parallel port control port
 ctr_byte = inp(CTR)
 Return ctr_byte  ; bit 8 of this byte is the low active contact switch
End Function







; Functions to utilize BIOS input/output, to access the printer port on the hardware level.
Function outp(adress, value)
 DlPortWritePortUchar(adress , value )
End Function


Function inp(adress)
 value = DlPortReadPortUchar%( adress )
 Return value
End Function









Function MotorHome(motor,speed#=0.5)
 reached_ref=0
 count_pulses=0
 While reached_ref=0
  ; drive slide to contact switch:
  code=sync_timer(speed#) ; this will sync the motor calling with a certain frequency (Hz = speed * 1000)
  If code=777 Then:Return code:EndIf ; (aborted by user?)
  status = call_motor(motor, 1)
  count_pulses=count_pulses+1
  If (status And 128)=0  ;(reached contact switch ?)
   While (status And 128)=0 ; drive a little back, again out of contact 
    code=sync_timer(speed#)
    If code=777 Then:Return code:EndIf
    status = call_motor(motor, 0)
    count_pulses=count_pulses-1
   Wend
   counter=0
   While counter<1600 ; add some extra mm (eg. ~2 turns with 1.8deg steppermotors and M5 lathe axis)
    code=sync_timer(speed#)
    If code=777 Then:Return code:EndIf
    status = call_motor(motor, 0)
    counter=counter+1
    count_pulses=count_pulses-1
   Wend
   reached_ref=1
  EndIf
 Wend
 Special_msg$="Last Motor Home required: "+ count_pulses +" Pulses." ; this is useful for metric calibrations!
 Return 0
End Function






; This function will wait for a certain while in order to synchronize
; the calling of the motors with the choosen speed (Hz=speed*1000)
Function sync_timer(local_speed#=1.0)
 secs=(1.0/local_speed#)-1
 t=MilliSecs()+secs
 While t >= MilliSecs()
  If KeyDown(57) ; allow space key to abort things
   Delay 200
   FlushKeys()
   Return 777  ; return some abort code
  EndIf
 Wend
 Return 0 ; return zero for "successful execution"
End Function





Function SetMotorPower(motor,power)
 ; (For the Bits Description see also SMC800 Manual)
 If motor<0 Then motor=0
 If motor>max_motor Then motor=max_motor

 ; 20% power (set default anyway)
 phase_A(motor,0)= %111
 phase_A(motor,1)= %110
 phase_A(motor,2)= %110
 phase_A(motor,3)= %110
 phase_A(motor,4)= %011
 phase_A(motor,5)= %010
 phase_A(motor,6)= %010
 phase_A(motor,7)= %010
 phase_A(motor,8)= phase_A(motor,0)

 phase_B(motor,0)= %110000
 phase_B(motor,1)= %110000
 phase_B(motor,2)= %011000
 phase_B(motor,3)= %010000
 phase_B(motor,4)= %010000
 phase_B(motor,5)= %010000
 phase_B(motor,6)= %111000
 phase_B(motor,7)= %110000
 phase_B(motor,8)= phase_B(motor,0)

 If power=50 Then
  ; 50% power
  phase_A(motor,0)= %111
  phase_A(motor,1)= %101
  phase_A(motor,2)= %101
  phase_A(motor,3)= %101
  phase_A(motor,4)= %011
  phase_A(motor,5)= %001
  phase_A(motor,6)= %001
  phase_A(motor,7)= %001
  phase_A(motor,8)= phase_A(motor,0)

  phase_B(motor,0)= %101000
  phase_B(motor,1)= %101000
  phase_B(motor,2)= %011000
  phase_B(motor,3)= %001000
  phase_B(motor,4)= %001000
  phase_B(motor,5)= %001000
  phase_B(motor,6)= %111000
  phase_B(motor,7)= %101000
  phase_B(motor,8)= phase_B(motor,0)
 EndIf
 If power=100 Then
  ; 100% power
  phase_A(motor,0)= %111
  phase_A(motor,1)= %100
  phase_A(motor,2)= %100
  phase_A(motor,3)= %100
  phase_A(motor,4)= %011
  phase_A(motor,5)= %000
  phase_A(motor,6)= %000
  phase_A(motor,7)= %000
  phase_A(motor,8)= phase_A(motor,0)

  phase_B(motor,0)= %100000
  phase_B(motor,1)= %100000
  phase_B(motor,2)= %011000
  phase_B(motor,3)= %000000 
  phase_B(motor,4)= %000000
  phase_B(motor,5)= %000000
  phase_B(motor,6)= %111000
  phase_B(motor,7)= %100000
  phase_B(motor,8)= phase_B(motor,0)
 EndIf

End Function

Comments

M2PLAY2007
WOUUUU!!! Amazing system. Good stuff!!!
Thanks


Wayne2007
Great stuff jfk!


Blitzplotter2007
outrageous, this sort of real world integration is very interesting. now to find a cnc milling machine....


jfk EO-111102007
Note the code only works with SMC type controllers. Other systems may even be simpler (they usually take only direction and speed parameters and generate the amplitude curves by their own, onboard).

There is one thing you should know about CNC, before you buy a machine: it takes many hours to make a single artpiece, especialy when you are using some kind of 3D raster milling application. Then again, it's great fun to drink a coffee while the machine is doing a lot of physical precision work.

The mentioned Proxxon is best used for tiny things. Max travel is 162 * 84 * 86 mm (XYZ). See here:
http://www.usovo.de


An other nice mill is the Taig CNC. Tho, shipping costs from the US to Europe was too high for me.

I can only suggest to look around for a while, there are plenty cheap mills (near 2k$), but it takes some time to find them all.
Here's however a comparation table:
http://www.desktopcnc.com/mill_table.htm

PS additional Note for people who want to use this code with the Proxxon MF70 CNC by usovo.de : The machine has a built in relay to start/stop the drill motor. It can be triggered with bit 2 (value $02) on the status port (LPT+2). Note you have to OR the handshake byte-output in call_motor() with the desired/required drill motor state.

PS2: The axis inverter array is now set to the proxxon motors layout, although in the zipfile it's still the old example. You may however carefully test your motors axes with the manual controls (cursor keys, PgUp/Dn) and edit the source until the motors act as desired BEFORE you hit "h" that will (try to) home all motors.


Danny2007
Wicked stuff jfk!!
Compliments!


LAB[au]2007
Yep nice work! I have the same machine (proxxon modded by Usovo) . I use it to make pcb and to engrave aluminium, until now I have used the recommended app for control (WinPCNC lite), now I could start to drive it more algorythmically :)

Thanks for the release.


jhocking2008
Whoah this is very cool. I'm interested in exploring different kinds of IO with Blitz, so this work that you are doing is great to see.


Andres2008
Is it possible to control the pins of LPT with something like that? I'd like to advance my PC-timer or control text on LCD from a calculator.


jfk EO-111102008
Yes sure simply use the portio.dll and decls that is part of the zipfile. You'll use the function INP and OUTP to write bytes to port adresses, such as your LPT port (see bios). The bits represent the pins (at least for the 8 Data bits). There are other ports, such as the LPT control port and status port, with bits that are organized slightly diffrent. You'll find some infos about pins and bits in the web. This one, although german, may be useful: http://www.netzmafia.de/skripten/hardware/rp1/rp12.html


Code Archives Forum