Linistepper Custom Software Mod by Kai Ries

[In my application] 5.25" floppy drive steppers are connected at each linistep driver. They run at 130 mA current each. I have used 10k pulldown resistors at the input instead of the 390 ohm resistors. The motors run smootly at about 2 rpm with 100uF ramping capacitors. I am working at an overvoltage circuit (30V,0.7A) to get more than 6 revs/sec. It is for use of a dual axis telescope drive (1:3000 gear ratio). I have modified the firmware to support only the microstepping mode and your special halfstepping mode [for use with Mel Bartels scope.exe^] Microstepping is very accurate and what is more important, there are absolutely NO vibrations, which can be even more distracting than rough step sizes.

Here is the source code of the mod. I have also included a ready made hex file for burning on a 16F84A.

The differences between the original and the modified firmware are as follows:

When you switch from the microstep mode to the halfstep mode, it always starts at the position between the fullsteps, because both coil A and B are on. Depending on where we have left the microstep position, we gain or lose one step. This has no great effect on the application here, because we only switch after long slews. Beside this, there is a gear of 1:3000 at the stepper shaft in this application. So there is no great loss. However, users should be generally aware of this.

I have [also] made a standalone controller based on the 16F84. It runs at 12 Mhz. It will drive an alt-azimuth nicely. It has quite a lot of features:

Kind regards,

Kai Ries

linistpv1.asm


;******************************************************************************
;
;  LiniStepper v1.1s
;
;  PIC 16F84A code
;
;  Copyright Aug 2002 Roman Black   www.romanblack.com
;
;  PIC assembler code for the LiniStepper stepper motor driver board.
;  400/3600 steps
;
;  v1.0	Seems to be working ok for now, few minor things need improving;
;		* touch up phase switching for direction -> 0
;		* table system is messy, can reduce in size a LOT if needed
;		* low-power mode doesn't microstep, only halfstep
;		* no easy way to step motor from within PIC software
;  
;  (set mplab TABS to 5 for best viewing this .asm file)
;  
;
;   	v1.1s Modifications for use with Mel Bartels scope.exe or the LiniStar controller
;	
;	v1.1 has been slightly modified to meet minimal telescope driving requirements, that is 
;	a fast quarterstepmode and a 18th microstepmode. The two mode inputs can be used for 
;	general purposes. 

;	General overview of program loop:
;	move_motor-->power variable check-->high/low power PCL-table--->pwm--->new_inputs-->move_motor---|
;							|--				    <--|							   |
;	|<---																		<---		

;	In the routine "move_motor" the PCL 'jump variable' steptemp is calculated and the corresponding
;	phase variable. The "pwm" routine has been left, when an input change has been detected. It´s timing 
;	is critical, since he currents are modulated this way.  
;==============================================================================
; 	mplab settings

	ERRORLEVEL -224		; suppress annoying message because of option/tris
	ERRORLEVEL -302		; suppress message because of bank select in setup ports

	LIST b=5, n=97, t=ON, st=OFF		;
	; absolute listing tabs=5, lines=97, trim long lines=ON, symbol table=OFF

;==============================================================================
; processor defined

	include <p16f84A.inc>
	

; processor config

	IFDEF __16F84A
		__CONFIG   _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC
	ENDIF
	


;==============================================================================
; Variables here

	;-------------------------------------------------
	IFDEF __16F84A
		#define RAM_START	0x0C
		#define RAM_END	RAM_START+d'68' 		; 16F84 has only 68 ram
	ENDIF

	;-------------------------------------------------
	CBLOCK 	RAM_START

		status_temp		; used for int servicing
		w_temp			; used for int servicing

		step				; (0-71) ustep position!
		steptemp			; for calcs
		steplp			; (0-3) half step position!
			
		
		phase			; stores the 4 motor phase pins 0000xxxx
	
		current1			; for current tween pwm
		current2			; for current tween pwm

		inputs			; stores new input pins
		inputs_last		; stores last states of input pins

	ENDC

	;-------------------------------------------------
	; PIC input pins for porta, these are the bits numbers

	#define 	STEP			0		; / = move 1 step, \=do nothing
	#define 	DIR			1		; lo= cw,  hi=ccw
	#define 	POWER		2		; lo=low power, hi=full power
	#define   INDEX		3		; index signal
	;-------------------------------------------------
	; Custom instructions!

	#define	skpwne		skpnz			; after subxx, uses zero
	#define	skpweq		skpz				; after subxx, uses zero
	#define	skpwle		skpc				; after subxx, uses carry
	#define	skpwgt		skpnc			; after subxx, uses carry

;==============================================================================
; CODE GOES HERE

	org 0x0000 			; Set program memory base at reset vector 0x00
reset
	goto main				;

;==============================================================================
move_motor				; goto label

						; find which of the 4 ranges we are in
	movf step,w			; get step
	movwf steptemp			; store as working temp
	movf steptemp,w		;
	sublw d'35'			; sub to test
	skpwle				;
	goto half_hi			; wgt, steptemp is 36-71 (upper half)

	;-------------------------
half_low					; wle, steptemp is 0-35

	movf steptemp,w		;
	sublw d'17'			; sub to test
	skpwle				;
	goto range1			; wgt
	
range0					; wle
	movlw b'00000101'		; 0101 = A+ B+
	goto phase_done		;

range1
	movlw b'00001001'		; 1001 = A- B+
	goto phase_done		;

	;-------------------------
half_hi					; steptemp is 36-71
						; NOTE! must subtract 36 from steptemp, so it
						; will become 0-35 and ok with table later!
	movlw d'36'			; subtract 36 from steptemp,
	subwf steptemp,f		; (now steptemp is 0-35)

						; now find the range
	movf steptemp,w		;
	sublw d'17'			; sub to test
	skpwle				;
	goto range3			; wgt
	
range2					; wle
	movlw b'00001010'		; 1010 = A- B-
	goto phase_done		;

range3
	movlw b'00000110'		; 0110 = A+ B-

phase_done				; note! steptemp is always 0-35 by here
	movwf phase			; store phase values

;-------------------------------------------------


	btfss inputs,POWER		; select table to use, check input here
	goto table_lowpower		; skip this if POWER pin is high

	;-------------------------------------------------
	; HIGH POWER TABLE
	; the PCL table is used, we should make sure that a valid value is sent to the PCL counter, otherwise
	; it jumps in arbitrarily place in the program
	;-------------------------------------------------

table_highpower			;

	movf steptemp,w		; add steptemp to the PCL
	addwf PCL,f			; 
						; here are the 36 possible values;
	;-------------------------
	goto st00				; * (hardware 6th steps)
	goto st01				;   (pwm tween steps)
	goto st02				;   (pwm tween steps)
	goto st03				; *
	goto st04				; 
	goto st05				; 

	goto st06				; *
	goto st07				;
	goto st08				;
	goto st09				; *
	goto st10				;
	goto st11				;

	goto st12				; *
	goto st13				;
	goto st14				;
	goto st15				; *
	goto st16				;
	goto st17				;

	goto st18				; *
	goto st19				;
	goto st20				;
	goto st21				; *
	goto st22				;
	goto st23				;

	goto st24				; *
	goto st25				;
	goto st26				;
	goto st27				; *
	goto st28				;
	goto st29				;

	goto st30				; *
	goto st31				;
	goto st32				;
	goto st33				; *
	goto st34				;
	goto st35				;

table_lowpower				;

	movf steplp,w		; add this step variable to the PCL
	addwf PCL,f			; 
						; here are the 4 possible values;
	
	goto lp00				;	55,25 (100,45) current low (high)
	goto lp09				;	25,55 (45,100)
	goto lp18				;	25,55 (45,100)
	goto lp27				;	55,25 (100,45)
	


st00						; (6th step)
	movf phase,w			; get coil phasing (is 0000xxxx)
	iorlw b'11000000'		; set currents; 100,0 
	movwf current2			;
	movwf current1			;
	goto pwm				;

st01						
	movf phase,w			; get coil phasing
	iorlw b'11000000'		; set 100,0 
	movwf current2			;
	movf phase,w			;
	iorlw b'11010000'		; set 100,25 
	movwf current1			;
	goto pwm				;

st02						
	movf phase,w			; get coil phasing
	iorlw b'11010000'		; set 100,25 
	movwf current2			;
	movf phase,w			;
	iorlw b'11000000'		; set 100,0 
	movwf current1			;
	goto pwm				;

	;-------------------------

st03						; (6th step)
	movf phase,w			;
	iorlw b'11010000'		; set 100,25 
	movwf current2			;
	movwf current1			;
	goto pwm				;

st04						;
	movf phase,w			;
	iorlw b'11010000'		; set 100,25 
	movwf current2			;
	movf phase,w			;
	iorlw b'11100000'		; set 100,55 
	movwf current1			;
	goto pwm				;

st05						;
	movf phase,w			;
	iorlw b'11100000'		; set 100,55 
	movwf current2			;
	movf phase,w			;
	iorlw b'11010000'		; set 100,25 
	movwf current1			;
	goto pwm				;

	;-------------------------

st06						; (6th step)
	movf phase,w			;
	iorlw b'11100000'		; set 100,55 
	movwf current2			;
	movwf current1			;
	goto pwm				;

st07						;
	movf phase,w			;
	iorlw b'11100000'		; set 100,55 
	movwf current2			;
	movf phase,w			;
	iorlw b'11110000'		; set 100,100 
	movwf current1			;
	goto pwm				;

st08						;
	movf phase,w			;
	iorlw b'11110000'		; set 100,100
	movwf current2			;
	movf phase,w			;
	iorlw b'11100000'		; set 100,55 
	movwf current1			;
	goto pwm				;

	;-------------------------

st09						; (6th step)
	movf phase,w			;
	iorlw b'11110000'		; set 100,100 
	movwf current2			;
	movwf current1			;
	goto pwm				;

st10						;
	movf phase,w			;
	iorlw b'11110000'		; set 100,100 
	movwf current2			;
	movf phase,w			;
	iorlw b'10110000'		; set 55,100 
	movwf current1			;
	goto pwm				;

st11						;
	movf phase,w			;
	iorlw b'10110000'		; set 55,100
	movwf current2			;
	movf phase,w			;
	iorlw b'11110000'		; set 100,100 
	movwf current1			;
	goto pwm				;

	;-------------------------

st12						; (6th step)
	movf phase,w			;
	iorlw b'10110000'		; set 55,100 
	movwf current2			;
	movwf current1			;
	goto pwm				;

st13						;
	movf phase,w			;
	iorlw b'10110000'		; set 55,100 
	movwf current2			;
	movf phase,w			;
	iorlw b'01110000'		; set 25,100 
	movwf current1			;
	goto pwm				;

st14						;
	movf phase,w			;
	iorlw b'01110000'		; set 25,100
	movwf current2			;
	movf phase,w			;
	iorlw b'10110000'		; set 55,100 
	movwf current1			;
	goto pwm				;

	;-------------------------
st15						; (6th step)
	movf phase,w			;
	iorlw b'01110000'		; set 25,100 
	movwf current2			;
	movwf current1			;
	goto pwm				;

st16						;
	movf phase,w			;
	iorlw b'01110000'		; set 25,100 
	movwf current2			;
	movf phase,w			;
	iorlw b'00110000'		; set 0,100 
	movwf current1			;
	goto pwm				;

st17						;
	movf phase,w			;
	iorlw b'00110000'		; set 0,100
	movwf current2			;
	movf phase,w			;
	iorlw b'01110000'		; set 25,100 
	movwf current1			;
	goto pwm				;

	;-------------------------
	;-------------------------

st18						; (6th step)
	movf phase,w			;
	iorlw b'00110000'		; set 0,100 
	movwf current2			;
	movwf current1			;
	goto pwm				;

st19						;
	movf phase,w			;
	iorlw b'00110000'		; set 0,100 
	movwf current2			;
	movf phase,w			;
	iorlw b'01110000'		; set 25,100 
	movwf current1			;
	goto pwm				;

st20						;
	movf phase,w			;
	iorlw b'01110000'		; set 25,100
	movwf current2			;
	movf phase,w			;
	iorlw b'00110000'		; set 0,100 
	movwf current1			;
	goto pwm				;

	;-------------------------

st21						; (6th step)
	movf phase,w			;
	iorlw b'01110000'		; set 25,100 
	movwf current2			;
	movwf current1			;
	goto pwm				;

st22						;
	movf phase,w			;
	iorlw b'01110000'		; set 25,100 
	movwf current2			;
	movf phase,w			;
	iorlw b'10110000'		; set 55,100 
	movwf current1			;
	goto pwm				;

st23						;
	movf phase,w			;
	iorlw b'10110000'		; set 55,100
	movwf current2			;
	movf phase,w			;
	iorlw b'01110000'		; set 25,100 
	movwf current1			;
	goto pwm				;

	;-------------------------

st24						; (6th step)
	movf phase,w			;
	iorlw b'10110000'		; set 55,100 
	movwf current2			;
	movwf current1			;
	goto pwm				;

st25						;
	movf phase,w			;
	iorlw b'10110000'		; set 55,100 
	movwf current2			;
	movf phase,w			;
	iorlw b'11110000'		; set 100,100 
	movwf current1			;
	goto pwm				;

st26						;
	movf phase,w			;
	iorlw b'11110000'		; set 100,100
	movwf current2			;
	movf phase,w			;
	iorlw b'10110000'		; set 55,100 
	movwf current1			;
	goto pwm				;

	;-------------------------

st27						; (6th step)
	movf phase,w			;
	iorlw b'11110000'		; set 100,100 
	movwf current2			;
	movwf current1			;
	goto pwm				;

st28						;
	movf phase,w			;
	iorlw b'11110000'		; set 100,100 
	movwf current2			;
	movf phase,w			;
	iorlw b'11100000'		; set 100,55 
	movwf current1			;
	goto pwm				;

st29						;
	movf phase,w			;
	iorlw b'11100000'		; set 100,55
	movwf current2			;
	movf phase,w			;
	iorlw b'11110000'		; set 100,100 
	movwf current1			;
	goto pwm				;

	;-------------------------

st30						; (6th step)
	movf phase,w			;
	iorlw b'11100000'		; set 100,55 
	movwf current2			;
	movwf current1			;
	goto pwm				;

st31						;
	movf phase,w			;
	iorlw b'11100000'		; set 100,55 
	movwf current2			;
	movf phase,w			;
	iorlw b'11010000'		; set 100,25 
	movwf current1			;
	goto pwm				;

st32						;
	movf phase,w			;
	iorlw b'11010000'		; set 100,25
	movwf current2			;
	movf phase,w			;
	iorlw b'11100000'		; set 100,55 
	movwf current1			;
	goto pwm				;

	;-------------------------

st33						; (6th step)
	movf phase,w			;
	iorlw b'11010000'		; set 100,25 
	movwf current2			;
	movwf current1			;
	goto pwm				;

st34						;
	movf phase,w			;
	iorlw b'11010000'		; set 100,25 
	movwf current2			;
	movf phase,w			;
	iorlw b'11000000'		; set 100,0 
	movwf current1			;
	goto pwm				;

st35						;
	movf phase,w			;
	iorlw b'11000000'		; set 100,0
	movwf current2			;
	movf phase,w			;
	iorlw b'11010000'		; set 100,25 
	movwf current1			;
	goto pwm				;
						; high power table done!


	;-------------------------------------------------
	; next are the 4 code fragments for the low power table.
	;-------------------------------------------------

lp00						;
	movlw b'10010101'		; 
	movwf current2			;
	movwf current1			;
	goto pwm				;

lp09						;
	movlw b'01101001'		;
	movwf current2			;
	movwf current1			;
	goto pwm				;

lp18						;
	movlw b'01101010'		;
	movwf current2			;
	movwf current1			;
	goto pwm				;

lp27						;
	movlw b'10010110'		;
	movwf current2			;
	movwf current1			;
	goto pwm				;

	;-------------------------------------------------


;------------------------------------------------------------------------------




;******************************************************************************
;  Main 
;******************************************************************************
;
;------------------
main						; goto label
;------------------

	;---------------------------------------------
						; do initial setup for ports and ints and stuff
	call setup			; this is our only proper call...
						; it is called only once, and does not really need
						; to be a function.
	;---------------------------------------------
	; main operating loop is here.
	;---------------------------------------------

	goto move_motor		; will set the motor to step 0,
						; and loop permanently from there

	;---------------------------------------------
	goto main				; safe loop, should never get here anyway.



;******************************************************************************
; NEW INPUTS   input change was detected
;******************************************************************************
;
;------------------
new_inputs				; goto tag
;------------------

	;-------------------------------------------------
	; when we enter here:
	; * one or more PORTA inputs have just changed
	; * inputs_last	contains last PORTA inputs values
	; * inputs		contains new PORTA inputs values
	;-------------------------------------------------
	; must first detect which input pins changed.

	; ---x----	RA4	*future use
	; ----x---	RA3	*future use
	
	; -----x--	RA2	* power, low=hi torque half stepping, hi=3600 mode
	; ------x-	RA1	* direction
	; -------x	RA0	* step

	; if step went hi, we move the step (step++ or step--)

	; if step went low:
	; ignore change in direction pin
	; ignore change in power pin
	; (all pins besides step are handled automatically in move_motor)
	;-------------------------------------------------

	movf inputs,w			; xor to compare new inputs with last values
	xorwf inputs_last,f		; now inputs_last has the diff.

	btfss inputs_last,STEP	; test if step input changed
	goto ni_end			; 

						; step input changed!
	btfss inputs,STEP		; test if change was lo-hi or hi-lo
	goto ni_end			; hi-lo, so ignore

	btfss inputs,POWER			; test power mode bit
	goto mode_lo			;

; 3600 mode (72/1)
; each step is 1

	btfss inputs,DIR		; test direction input
	goto m36_up			;

m36_down
	decf step,f			; step--
	btfss step,7			; test for roll under <0
	goto ni_end			; ok
						; rolled under!
	movlw d'71'			; force to top step (72-1)
	movwf step			;
	goto ni_end			;

m36_up
	incf step,f			; step++
	movf step,w			; test for roll over >71
	sublw d'71'			; sub to test
	skpwle				;
	clrf step				; wgt, rolled over so force to step 0

	goto ni_end			;

	;-------------------------------------------------
;For low power mode there are 4 entries only, so this is easy
mode_lo
	btfss inputs,DIR		; test direction input
	goto m40_up

m40_down
	decf steplp,f			; steplp--
	btfss steplp,7			; test for roll under <0
	goto ni_end			; ok
						; rolled under!
	movlw d'3'			; force to top step (4-1)
	movwf steplp			;
	goto ni_end			;

m40_up
	incf steplp,f			; steplp++
	movf steplp,w			; test for roll over >3
	sublw d'3'			; sub to test
	skpwle				;
	clrf steplp			; wgt, rolled over so force to step 0

	goto ni_end			;


ni_end
	movf inputs,w			; save a copy of the inputs
	movwf inputs_last		;

	goto move_motor		; go and make it all happen

;------------------------------------------------------------------------------
;******************************************************************************
; PWM		is the fast pwm loop
;******************************************************************************

pwm_loop
						; first output current1 to motor
	movf current1,w		; get currents and phase switching
	movwf PORTB			; send to motor!

	nop					; timing delay
	nop					;
						; (4 cycles)
	;-------------------------
pwm						; main entry!
						; better to enter at current2 for motor power.

						; now output current2
	movf current2,w		;
	movwf PORTB			; send to motor!
	nop					; safe wait 250nS

						; now test input pins
	movf PORTA,w			; get pin values from port

	xorwf inputs_last,w		; xor to compare new inputs with last values
	skpnz
	goto pwm_loop			; z, inputs not changed, so keep looping
						; (8 cycles)
	;-------------------------------------------------
						; nz, one or more input pins have changed!
	xorwf inputs_last,w		; restore xored value back to the orig inputs value
	movwf inputs			;

	goto new_inputs		; 
	;-------------------------------------------------

;------------------------------------------------------------------------------
;******************************************************************************
;  SETUP   sets port directions and interrupt stuff etc,
;******************************************************************************
; NOTE!! is the only proper funtion, is done before other activity

;------------------
setup					; routine tag
;------------------


						; OPTION setup
	movlw b'10000010'		;
	
	banksel OPTION_REG		; go proper reg bank
	movwf OPTION_REG		; load data into OPTION_REG
	banksel 0				;


	;-------------------------------------------------
						; PORTB pins direction setup
						; 1=input, 0=output
	clrf PORTB			;
						;
	movlw b'00000000'		; all 8 portb are outputs
						;
	banksel TRISB			; go proper reg bank
	movwf TRISB			; send mask to portb
	banksel 0				;
	;-------------------------------------------------

						; PORTA pins direction setup
						; 1=input, 0=output
	clrf PORTA			;

						; NOTE!! only three PORTA pins are used as inputs now
	movlw b'00000111'		;
		;  ---x----		; RA4
		;  ----x---		; RA3
		;  -----x--		; RA2 POWER
		;  ------x-		; RA1 DIRECTION
		;  -------x		; RA0 STEP

	banksel TRISA			; go proper reg bank
	movwf TRISA			; send mask to porta
	banksel 0				;
	;-------------------------------------------------

	movlw 0x00			; set up PCLATH for all jump tables on page 0
	movwf PCLATH			; (all tables are in move_motor)
	;-------------------------------------------------

						; CLEAR RAM! for lower bank
	movlw RAM_START		; first byte of ram
	movwf FSR				; load pointer
ram_clear_loop
	clrf INDF				; clear the ram we pointed to
	incf FSR,f			; inc pointer to next ram byte
	movf FSR,w			; get copy of pointer to w
	sublw RAM_END			; test if PAST the last byte now
	skpweq				;
	goto ram_clear_loop		;

	;-------------------------------------------------
						; here we can set the user variables and output pins

	movlw 0x00			; for step 0 of 0-71
	movwf step			; loaded ready for jump table

	movf PORTA,w			; get initial value for inputs
	movwf inputs			;
	movwf inputs_last		;

	;-------------------------------------------------
						; set up INTCON register last
	movlw b'00000000'		; set the bit value 

		;  x-------		; bit7 	GIE global int enable, 1=enabled
		;  -x------		; bit6	EE write complete enable, 1=en
		;  --x-----		; bit5 	TMR0 overflow int enable, 1=en
		;  ---x----		; bit4 	RB0/INT enable, 1=en
		;  ----x---		; bit3	RB port change int enable, 1=en
		;  -----x--		; bit2	TMR0 int flag bit, 1=did overflow and get int
		;  ------x-		; bit1	RB0/INT flag bit, 1=did get int
		;  -------x		; bit0	RB port int flag bit, 1=did get int

	movwf INTCON			; put in INTCON register
	;-------------------------------------------------
	return				;
;------------------------------------------------------------------------------



	end