;########################################################
;#        Dual Unipolar Stepper Motor Controller        #
;#                  for PIC16F876A                      #
;#                   by Hazzer123                       #
;#                version - 03.02.08                    #	
;#                                                      #
;#        Stepper motor 1 connected to PORTA<3:0>       #
;#        Stepper motor 2 connected to PORTB<3:0>       #
;#                                                      #
;#     For more great robot related information and     #
;#       discussion visit www.societyofrobots.com       #
;########################################################

 LIST    p=16F876A ; PIC16F844 is the target processor

	#include "P16F876A.INC"

#define DIR				0
#define	MOVING			1
#define	STATE			2

	cblock 0x20   ; Temporary storage
	d1
	d2
	d3
	MOTOR1CON
	MOTOR2CON
	MOTOR1SPEED
	MOTOR1SPEEDBUFF
	MOTOR2SPEED
	MOTOR2SPEEDBUFF
	MOTOR1STEPNUM
	MOTOR2STEPNUM
	counter
	endc

        org   0
        goto	Start

;INTERRUPT ROUTINE==========================================		
		org		0x04
		BTFSC	INTCON,TMR0IF
		CALL	timer_0_overflow
		BTFSC	PIR1,TMR1IF
		CALL	timer_1_overflow
		RETFIE


;START======================================================
Start	BCF		STATUS,RP0
		BCF		STATUS,RP1		;Page 0

;PORTS======================================================
		
		BSF		STATUS,RP0
		MOVLW 	0x06			;Set PORTA to digital
		MOVWF 	ADCON1
		MOVLW 	b'11110000'		;RA0-3 outputs to ULN2003 for motor 1
		MOVWF 	TRISA
		MOVLW	b'11110000'		;RB0-3 outputs to ULN2003 for motor 2
		MOVWF	TRISB
		MOVLW	b'00011000'		;RC3 and RC4 inputs for I2C slave mode
		MOVWF	TRISC
		BCF		STATUS,RP0
		CLRF 	PORTA			;Clear ports
		CLRF	PORTB
		CLRF	PORTC

;TIMER0======================================================

		BSF		STATUS,RP0
		MOVLW	b'11000100'		;TMR0 on, 1:32 prescaler
		MOVWF	OPTION_REG
		BCF		STATUS,RP0
		BCF		INTCON,TMR0IF	;Clear Timer 0 flag bit
		BSF		INTCON,TMR0IE	;Enable Timer0 tnterrupts

;TIMER1======================================================

		MOVLW	b'00000001'		;TMR1 on, no prescaler
		MOVWF	T1CON
		BCF		PIR1,TMR1IF		;Clear Timer 1 flag bit
		BSF		STATUS,RP0
		BSF		PIE1,TMR1IE		;Enable Timer 1 interrupts
		BCF		STATUS,RP0

;INITIALISE VARIABLES=========================================

		MOVLW	d'10'			;Initial time between half-steps is 10ms - MOTORXSPEED doesnt really hold a value for speed, but the time between sequential half-steps
		MOVWF	MOTOR1SPEED
		MOVWF	MOTOR1SPEEDBUFF
		MOVLW	d'10'
		MOVWF	MOTOR2SPEED
		MOVWF	MOTOR2SPEEDBUFF
		
		BSF		MOTOR1CON,DIR	;Motor 1 is default forward
		BCF		MOTOR1CON,MOVING;Motor 1 is default not moving
		BCF		MOTOR1CON,STATE	;Motor 1 is default turned off.

		BSF		MOTOR2CON,DIR
		BCF		MOTOR2CON,MOVING
		BCF		MOTOR2CON,STATE

		MOVLW	d'1'
		MOVWF	MOTOR1STEPNUM	;Both start with stepnum value 1
		MOVWF	MOTOR2STEPNUM

		clrf	counter			;clear counter

;INTERRUPTS===================================================
		
		BSF		INTCON,TMR0IE	;Enable timer0 interrupt
		BSF		INTCON,PEIE		;Enable all peripheral interrupts
		BSF		INTCON,GIE		;Enable all interrupts

;MAIN=========================================================
		
		BSF		MOTOR1CON,STATE		;Turn motors on and moving
		BSF		MOTOR2CON,STATE
		BSF		MOTOR1CON,MOVING
		BSF		MOTOR2CON,MOVING

Loop	incf	counter,f		
		movf	counter,w
		call	table_lookup	;Find the speed for motor 1 in the table lookup
		movwf	MOTOR1SPEED
		incf	counter,f
		movf	counter,w
		call	table_lookup	;Find the speed for motor 2 in the table lookup
		movwf	MOTOR2SPEED
		call	delay_1s		;Delay, to allow the motors to turn.
		goto 	Loop

table_lookup
		addwf	PCL
		nop
		retlw	6			;Speed of motor 1 in sequence. (Go forward)
		retlw	6			;Speed of motor 2 in sequence.
 
		retlw	6			;Second speed of motor 1 in sequence.	(Turn left)
		retlw	10			;Second speed of motor 2 in sequence....
 
		retlw	6			;(Go forward)
		retlw	6
 
		retlw	10			;(Turn right)
		retlw	6
		
		movlw	d'1'		;If counter is too high, reset and do table_lookup again
		movwf	counter
		goto	table_lookup
 
		RETURN
 




timer_0_overflow
		BCF		INTCON,TMR0IF		;Clear interrupt flag bit

		movlw	0x09				;Small inline delay, to make time between interrupts 1ms
		movwf	d1
delay_tmr0_0
		decfsz	d1, f
		goto	delay_tmr0_0
		goto	$+1

		MOVLW	d'101'				;Offset the timer 0, in order to get 1ms interrupts
		MOVWF	TMR0			

		DECFSZ	MOTOR1SPEEDBUFF,F	;Count down the milliseconds until the next half-step
		RETURN
		
		BTFSS	MOTOR1CON,STATE		;If the motor is turned off, clear porta
		CLRF	PORTA	

		BTFSS	MOTOR1CON,MOVING	;Return before half-step routine if not moving
		RETURN

		BTFSC	MOTOR1CON,DIR
		goto	aforwards
		BTFSS	MOTOR1CON,DIR
		goto	abackwards

aforwards
		INCF	MOTOR1STEPNUM,F		;Going forward so increment step num.
		MOVF	MOTOR1STEPNUM,W		;Check if step num has gone to 9 (too far)
		SUBLW	d'9'
		BTFSS	STATUS,Z
		GOTO	acarry_on				
		MOVLW	d'1'				;If counter = 9, loop back and set stepnum to 1.
		MOVWF	MOTOR1STEPNUM
		GOTO	acarry_on

abackwards
		DECFSZ	MOTOR1STEPNUM,F		;Going backwards so decrement step num
		GOTO	acarry_on			;If not zero, carry on.
		MOVLW	d'8'
		MOVWF	MOTOR1STEPNUM		;If zero, then loop around and set stepnum to 8
		GOTO	acarry_on

	
acarry_on	movf	MOTOR1STEPNUM,W	
		call	step_table			;Find corresponding PORT state for step num
		BTFSC	MOTOR1CON,STATE
		MOVWF	PORTA				;If motor is turned on, move port state to PORTA
		BTFSS	MOTOR1CON,STATE
		CLRF	PORTA				;If motor is turned off, clear PORTA
	
		MOVF	MOTOR1SPEED,W		;Load the motor speed into a buffer for counting down.
		MOVWF	MOTOR1SPEEDBUFF
		RETURN

timer_1_overflow
		BCF		PIR1,TMR1IF			;Basically the same as timer_0_overflow

		MOVLW	b'11101100'			;These values make time between interrupt 1ms
		MOVWF	TMR1H		
		MOVLW	b'10101001'
		MOVWF	TMR1L		

		DECFSZ	MOTOR2SPEEDBUFF,F
		RETURN

		BTFSS	MOTOR2CON,STATE
		CLRF	PORTB

		BTFSS	MOTOR2CON,MOVING
		RETURN

		BTFSC	MOTOR2CON,DIR
		goto	bforwards
		BTFSS	MOTOR2CON,DIR
		goto	bbackwards

bforwards
		DECFSZ	MOTOR2STEPNUM,F
		GOTO	bcarry_on
		MOVLW	d'8'
		MOVWF	MOTOR2STEPNUM
		GOTO	bcarry_on

bbackwards
		INCF	MOTOR2STEPNUM,F
		MOVF	MOTOR2STEPNUM,W
		SUBLW	d'9'
		BTFSS	STATUS,Z
		GOTO	bcarry_on
		MOVLW	d'1'
		MOVWF	MOTOR2STEPNUM
		GOTO	bcarry_on
	
bcarry_on	movf	MOTOR2STEPNUM,W
		call	step_table
		BTFSC	MOTOR2CON,STATE
		MOVWF	PORTB
		BTFSS	MOTOR2CON,STATE
		CLRF	PORTB
	
		MOVF	MOTOR2SPEED,W
		MOVWF	MOTOR2SPEEDBUFF
		RETURN

step_table
		addwf	PCL
		retlw	b'0000'
		retlw	b'1001'
		retlw	b'1000'
		retlw	b'1010'
		retlw	b'0010'
		retlw	b'0110'
		retlw	b'0100'
		retlw	b'0101'
		retlw	b'0001'
 
		RETURN



; Delay = 0.1 seconds
; Clock frequency = 20 MHz

; Actual delay = 0.1 seconds = 500000 cycles
; Error = 0 %

delay_100ms
			;499994 cycles
	movlw	0x03
	movwf	d1
	movlw	0x18
	movwf	d2
	movlw	0x02
	movwf	d3
delay_100ms_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	$+2
	decfsz	d3, f
	goto	delay_100ms_0

			;2 cycles
	goto	$+1

			;4 cycles (including call)
	return

; Delay = 1 seconds
; Clock frequency = 20 MHz

; Actual delay = 1 seconds = 5000000 cycles
; Error = 0 %

delay_1s
			;4999993 cycles
	movlw	0x2C
	movwf	d1
	movlw	0xE7
	movwf	d2
	movlw	0x0B
	movwf	d3
delay_1s_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	$+2
	decfsz	d3, f
	goto	delay_1s_0

			;3 cycles
	goto	$+1
	nop

			;4 cycles (including call)
	return

; Delay = 0.001 seconds
; Clock frequency = 20 MHz

; Actual delay = 0.001 seconds = 5000 cycles
; Error = 0 %


delay_wms
			;4993 cycles
	movwf	d3
delay_1ms
	movlw	0xE6
	movwf	d1
	movlw	0x04
	movwf	d2
delay_1ms_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	delay_1ms_0
	
	decfsz	d3,f
	goto	delay_1ms

			;3 cycles
	goto	$+1
	nop

			;4 cycles (including call)
	return

delay_quartturn
			;3249993 cycles
	movlw	0xAC
	movwf	d1
	movlw	0x30
	movwf	d2
	movlw	0x08
	movwf	d3
delay_quartturn_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	$+2
	decfsz	d3, f
	goto	delay_quartturn_0

			;3 cycles
	goto	$+1
	nop

			;4 cycles (including call)
	return
	end