Rotate stepper motor left and right

Thread Starter

bazaic

Joined Jan 6, 2014
5
Hi. I'm trying to rotate a step-by-step motor 20 times to left and 20 times to right and on and on. I'm using a PIC 16F917. I tried to make a program, but it only rotates the motor to the left. I used 2 variables one to retain 20 and one to use like a contor. Please help me see what is wrong.

Rich (BB code):
#INCLUDE P16F917.INC

MPP 		EQU 		20H
VFIX		EQU			0x70
			CBLOCK		0x20
			VAR
			ENDC

			ORG	 		0000H

RESET:		GOTO 		INIT

			ORG			0004H
			
MAIN:		
			INCF		VAR,W
			GOTO		RR
			MOVLW		VFIX
			SUBWF		VAR,W
			BTFSS		STATUS,Z
			GOTO		MAIN
			MOVWF		0
			MOVLW		VAR
CALL_RL:
			INCF		VAR,W
			GOTO		RL
			MOVLW		VFIX
			SUBWF		VAR,W
			BTFSS		STATUS,Z
			GOTO		CALL_RL
			MOVWF		0
			MOVLW		VAR

			GOTO		MAIN
			


RR:			BCF 		PIR1,CCP1IF
			RRF 		MPP,F 
			BCF 		MPP,7 
			BTFSC		STATUS,C
			BSF			MPP,7
			MOVF		MPP,W
			MOVWF		PORTD
			RETFIE

RL:			BCF 		PIR1,CCP1IF
			RLF 		MPP,F 
			BCF 		MPP,0 
			BTFSC		STATUS,C
			BSF			MPP,0
			MOVF		MPP,W
			MOVWF		PORTD
			RETFIE

INIT:		MOVLW		20
			MOVWF		VFIX
			MOVLW		0
			MOVWF		VAR

			CLRF		STATUS
			MOVLW		88H
			MOVWF		MPP
			
PD_INI:		CLRF		PORTD
			BSF			STATUS,RP0
			MOVLW		0FH
			MOVWF		TRISD			
			BCF			STATUS,RP0
			
INTINI:		MOVLW		0C0H
			MOVWF		INTCON
			BSF			STATUS,RP0
			MOVLW		04H
			BCF			STATUS,RP0
			CLRF		PIR1
			
T1_INI:		MOVLW		0AH
			MOVWF		T1CON
			CLRF		TMR1H
			CLRF		TMR1L
			
CCPINI:		MOVLW		0BH
			MOVWF		CCP1CON
			MOVLW		20H
			MOVWF		CCPR1H
			CLRF		CCPR1L
		
			BSF			T1CON,TMR1ON
			RETURN
			END
 

Attachments

Last edited by a moderator:

JohnInTX

Joined Jun 26, 2012
4,787
MPP and VAR are both located at 20h. Put MPP in the CBLOCK.

After reset, you goto INIT which sets some things up but RETURNS at the end - returns to where??? In actual use, it probably returns to 0000h, restarting every time. You need some loop construct.

MAIN is at the interrupt vector presumably servicing timer 1 interrupt. The code bumps VAR into W then jumps to RR. It will never execute anything below that (CALL_RL, RL etc.). I don't see where the incremented VAR (in W) is used as its clobbered in subsequent code. Note that incf VAR,W does NOT change the value of VAR in memory, it only increments it and places the result in W. Assuming it got past the GOTO, W is immediately clobbered by MOVLW VFIX.

You mix interrupt code with normal code including a GOTO 0 in the interrupt routine. You can't do that. Interrupts are to service time-sensitive events (in your case time to step the motor) from anywhere in the rest of the program. The service routine gets called when the event happens, you perform the function then return(with retfie) to the rest of the program. Since the interrupt can happen anywhere, you must save the context of the interrupted program (W, STATUS etc) and restore it before retfie. You do not do any context saving in the interrupt code, a sure recipe for disaster.

I would recommend that you revisit your design and create a good flow chart. Desk check the design by manually running through the flow chart with your finger. Find those places that don't make sense i.e. the RETURN at the end of INIT. Use better names i.e. MAIN is a name that everyone associates with the main program, not an interrupt.

Use descriptive names for your variables. VAR means what, a variable? I get that. What is its function? If it counts motor steps then name it MotorStepCounter. It will help you as you write and debug your code and also help others who are trying to understand it.

Use comments. After desk checking the flow chart, start writing your code by adding the comments FIRST i.e. ;Initialize timer 1 to 10msec interrupt, ;Step the motor clockwise by pulsing RD0 or whatever. Then under each comment block, add the code that makes it happen using the flow chart as a guide.

I think the fact that the motor runs at all is sheer luck so far but don't lose heart. Review your design, flow it out and trace the logic. Post the flow chart, revised code and schematic. We can go from there.

Good luck!
 
Last edited:

Thread Starter

bazaic

Joined Jan 6, 2014
5
I used your advice and I wrote the program again. Do you think that now it is correct?

Rich (BB code):
#INCLUDE P16F917.INC

MPP 			EQU 		20H
MOTORSTEPCOUNTER	EQU		24H
ROTATEFIXEDNUMBER	EQU		30H
SELECTROTATION		EQU		34H
			

			ORG	 	0000H

RESET:			GOTO 		INIT

			ORG		0004H					;JUMP ADDRESS FROP CCP IT OCCURS WHEN TMR1H = CCPR1H AND TMR1L = CCPR1L
	
MAIN:			DECFSZ		SELECTROTATION,0			;IF SELECTROTATION=0 THE MOTOR ROTATES ON THE RIGHT,ELSE THE MOTOR ROTATES ON THE LEFT												
			GOTO		LEFT_ROTATION
										;THE ROUTINE FOR RIGHT ROTATION
RIGHT_ROTATION:			
			INCF		MOTORSTEPCOUNTER,f			;INCREMENT MOTORSTEPCOUNTER AND SAVE THE CHANGE IN MOTORSTEPCOUNTER
			BCF 		PIR1,CCP1IF				;DELETE INTRERRUPT "FLAG"
			RRF 		MPP,F 					;ROTATE MPP VARIABLE THROUGH CARRY
			BCF 		MPP,7 					;PUT ON 0 THE BIT ENTERED FROM CARRY IN BIT 7 OF MPP
			BTFSC		STATUS,C				;IF CARRY=0, BIT 7 REMAINS THE SAME
			BSF		MPP,7					;ROTATE MPP WITHOUT CARRY
			MOVF		MPP,W					;MPP IS TAKEN OUT ON PORT D
			MOVWF		PORTD
			MOVLW		MOTORSTEPCOUNTER			;PUT IN W MOTORSTEPCOUNTER
			SUBWF		ROTATEFIXEDNUMBER,0			;SUBSTRACT: ROTATEFIXEDNUMBER-MOTORSETPCOUNTER
			BTFSS		STATUS,Z				;IF MOTORSTEPCOUNTER<ROTATEFIXEDNUMBER THEN WE RETURN TO GOTO $
			RETFIE
			MOVWF		0					;ELSE WE MAKE MOTORSTEPCOUNTER=0 AND THEN WE RETFIE TO GOTO $
			MOVLW		MOTORSTEPCOUNTER
			MOVWF		1
			MOVLW		SELECTROTATION
			RETFIE
									


										;THE ROUTINE FOR LEFT ROTATION
										;IT IS ALMOST THE SAME LIKE FOR THE RIGHT
LEFT_ROTATION:
			MOVLW		MOTORSTEPCOUNTER
			ADDLW		1
			MOVWF		MOTORSTEPCOUNTER
			BCF 		PIR1,CCP1IF
			RLF 		MPP,F 
			BCF 		MPP,0 
			BTFSC		STATUS,C
			BSF		MPP,0
			MOVF		MPP,W
			MOVWF		PORTD
			MOVLW		MOTORSTEPCOUNTER
			SUBWF		ROTATEFIXEDNUMBER,W
			BTFSS		STATUS,Z
			RETFIE
			MOVWF		0
			MOVLW		MOTORSTEPCOUNTER
			MOVWF		0
			MOVLW		SELECTROTATION
			RETFIE

			
										;INITIALIZE ROTATEFIXEDNUMBER AND MOTORSTEPCOUNTER
INIT:			MOVLW		20
			MOVWF		ROTATEFIXEDNUMBER
			MOVWL		0
			MOVWF		SELECTROTATION
			MOVLW		0
			MOVWF		MOTORSTEPCOUNTER
										;INITIALIZE MPP 
										;THE CODE BELOW I USED IT TO ROTATE THE MOTOR TO THE RIGHT SIDE AND IT WORKED;
			CLRF		STATUS
			MOVLW		88H
			MOVWF		MPP

			
PD_INI:			CLRF		PORTD
			BSF		STATUS,RP0
			MOVLW		0FH
			MOVWF		TRISD			
			BCF		STATUS,RP0
			

INTINI:			MOVLW		0C0H
			MOVWF		INTCON
			BSF		STATUS,RP0
			MOVLW		04H
			BCF		STATUS,RP0
			CLRF		PIR1
			

T1_INI:			MOVLW		0AH
			MOVWF		T1CON
			CLRF		TMR1H
			CLRF		TMR1L
			
CCPINI:			MOVLW		0BH
			MOVWF		CCP1CON
			MOVLW		20H
			MOVWF		CCPR1H
			CLRF		CCPR1L
		
			BSF		T1CON,TMR1ON
			GOTO		$
			END
 

Attachments

Last edited by a moderator:

JohnInTX

Joined Jun 26, 2012
4,787
Do you think that now it is correct?
I don't think it assembles due to the MOVWL instruction.
Have you tried it?
What are you using for IDE, assembler and debug?

After getting it to assemble, review the MOV-- instructions in the instruction set. You have confused some of them. For example:
Rich (BB code):
        MOVWF    0
        MOVLW    MOTORSTEPCOUNTER
        MOVWF    0
        MOVLW    SELECTROTATION
does not clear the two registers.
Rich (BB code):
        MOVLW    MOTORSTEPCOUNTER
        ADDLW    1
        MOVWF    MOTORSTEPCOUNTER
does not add 1 to MOTORSTEPCOUNTER. It actually writes 25h to MOTORSTEPCOUNTER every time. Do you see why?
Finally, review the ,F and ,W destination indicators:
DECFSZ SELECTROTATION,0
does not decrement SELECTROTATION because the result is placed in WREG.

EDIT: Looking at it again I see that you use DECFSZ SELECTROTATION as a left/right flag so placing the result in W is OK. However, the logic is wrong. When it =1, decrementing it will cause the code to fall through to RIGHT_ROTATION but at the end of the 20 steps to the right, you load it to 1 again instead of 0 to make it turn left. Actually, using a single-bit flag here would be more appropriate and easier to follow.

There are some other problems but lets start with those.
Have fun!
 
Last edited:

Thread Starter

bazaic

Joined Jan 6, 2014
5
I made some changes. how do I make the substract 20-valueof(RotateMotorCounter)? and do a conditional jump after?

I mean here
Rich (BB code):
			MOVLW		MOTORSTEPCOUNTER			;PUT IN W MOTORSTEPCOUNTER
			SUBLW		14H						;SUBSTRACT: ROTATEFIXEDNUMBER-MOTORSETPCOUNTER
			BTFSS		STATUS,Z				;IF MOTORSTEPCOUNTER<ROTATEFIXEDNUMBER THEN WE RETURN TO GOTO $
			GOTO		RETURNROTATE
Rich (BB code):
#INCLUDE P16F917.INC

MPP 			EQU 		20H
MOTORSTEPCOUNTER	EQU		24H

SELECTROTATION		EQU		34H
			

			ORG	 	0000H

RESET:		GOTO 		INIT

			ORG		0004H					;JUMP ADDRESS FROP CCP IT OCCURS WHEN TMR1H = CCPR1H AND TMR1L = CCPR1L
	
MAIN:		DECFSZ		SELECTROTATION,0			;IF SELECTROTATION=0 THE MOTOR ROTATES ON THE RIGHT,ELSE THE MOTOR ROTATES ON THE LEFT												
			GOTO		LEFT_ROTATION
										;THE ROUTINE FOR RIGHT ROTATION
RIGHT_ROTATION:			
			INCF		MOTORSTEPCOUNTER,f			;INCREMENT MOTORSTEPCOUNTER AND SAVE THE CHANGE IN MOTORSTEPCOUNTER
			BCF 		PIR1,CCP1IF				;DELETE INTRERRUPT "FLAG"
			RRF 		MPP,F 					;ROTATE MPP VARIABLE THROUGH CARRY
			BCF 		MPP,7 					;PUT ON 0 THE BIT ENTERED FROM CARRY IN BIT 7 OF MPP
			BTFSC		STATUS,C				;IF CARRY=0, BIT 7 REMAINS THE SAME
			BSF			MPP,7					;ROTATE MPP WITHOUT CARRY
			MOVF		MPP,W					;MPP IS TAKEN OUT ON PORT D
			MOVWF		PORTD
			MOVLW		MOTORSTEPCOUNTER			;PUT IN W MOTORSTEPCOUNTER
			SUBLW		14H						;SUBSTRACT: ROTATEFIXEDNUMBER-MOTORSETPCOUNTER
			BTFSS		STATUS,Z				;IF MOTORSTEPCOUNTER<ROTATEFIXEDNUMBER THEN WE RETURN TO GOTO $
			GOTO		RETURNROTATE
			MOVLW		00H					;ELSE WE MAKE MOTORSTEPCOUNTER=0 AND THEN WE RETFIE TO GOTO $
			MOVWF		MOTORSTEPCOUNTER
			MOVLW		02H
			MOVWF		SELECTROTATION
			GOTO		RETURNROTATE
									


										;THE ROUTINE FOR LEFT ROTATION
										;IT IS ALMOST THE SAME LIKE FOR THE RIGHT
LEFT_ROTATION:
			INCF		MOTORSTEPCOUNTER,f
			BCF 		PIR1,CCP1IF
			RLF 		MPP,F 
			BCF 		MPP,0 
			BTFSC		STATUS,C
			BSF			MPP,0
			MOVF		MPP,W
			MOVWF		PORTD
			MOVF		MOTORSTEPCOUNTER
			SUBLW		14H
			BTFSC		STATUS,Z
			GOTO		RETURNROTATE
			MOVLW		00H
			MOVWF		MOTORSTEPCOUNTER
			MOVLW		01H
			MOVWF		SELECTROTATION
			GOTO		RETURNROTATE

			
										;INITIALIZE ROTATEFIXEDNUMBER AND MOTORSTEPCOUNTER
INIT:		
			MOVLW		02H
			MOVWF		SELECTROTATION
			MOVLW		00H
			MOVWF		MOTORSTEPCOUNTER
										;INITIALIZE MPP 
										;THE CODE BELOW I USED IT TO ROTATE THE MOTOR TO THE RIGHT SIDE AND IT WORKED;
			CLRF		STATUS
			MOVLW		88H
			MOVWF		MPP

			
PD_INI:		CLRF		PORTD
			BSF		STATUS,RP0
			MOVLW		0FH
			MOVWF		TRISD			
			BCF		STATUS,RP0
			

INTINI:		MOVLW		0C0H
			MOVWF		INTCON
			BSF		STATUS,RP0
			MOVLW		04H
			BCF		STATUS,RP0
			CLRF		PIR1
			

T1_INI:		MOVLW		0AH
			MOVWF		T1CON
			CLRF		TMR1H
			CLRF		TMR1L
			
CCPINI:		MOVLW		0BH
			MOVWF		CCP1CON
			MOVLW		20H
			MOVWF		CCPR1H
			CLRF		CCPR1L
		
			BSF		T1CON,TMR1ON

RETURNROTATE:	MOVLW		0C0H		;ACTIVATE INTRERRUPTS
				MOVWF		INTCON
				GOTO		$
				END
 

JohnInTX

Joined Jun 26, 2012
4,787
I am not sure why you made the changes you did but the code in post #3 is workable, this one is not.

The primary problem is that you GOTO out of the interrupt routine to the non-interrupt routine. You can't do that, period. Interrupts push the interrupted routine's return address on the stack, retfie pops the stack and resumes execution of the interrupted routine. GOTO leaves the stack pushed. This code will run exactly 8 times before stack overflow and then the program is crashed. Again, if its running at all its pure luck.

To continue, stop writing code then:
Write and post a flow chart. EDIT: this is important, I flowed out your code by printing it, traceing the flow with a red pen, and found the problems in a jiffy. Your latest revision restores some problems that you had in the original code that likely would have jumped out in a good flow chart.
Re-read my posts until you understand the points presented. If you have questions about any points, I will answer them. EDIT: I don't mean to sound harsh here but you've missed some important things.
Revert to your post #3 code, its interrupt uses the stack correctly. EDIT: I also have it up as a project here and we can fix it together.

For your specific question:
Rich (BB code):
  MOVLW MOTORSTEPCOUNTER
  SUBLW 14h ; subtract W from LITERAL value (14h - motor steps so far) 
  BTFSS STATUS,Z 
  GOTO RETURNROTATE
MOVLW MOTORSTEPCOUNTER
This is incorrect. MOVLW puts the ADDRESS of the variable (24h) into W, not its value. To get its value use
Rich (BB code):
  MOVF MOTORSTEPCOUNTER,W ; move value into W
  SUBLW 14h ; subtract W from LITERAL value (14h - motor steps so far) 
  BTFSS STATUS,Z ; skip when Z set (they are equal)
  GOTO IRQreturn ; has not counted to 20 yet. Done, jump to the RETFIE

  ; process the next step then..

  GOTO IRQreturn ; jump to the RETFIE

 ; more code etc

IRQreturn:
 RETFIE ; return to the interrupted code - pops the stack, reenables the interrupt
A few more things:
Using DECFSZ as a flag is discouraged as its counter-intuitive. I see you modified the code but its still wrong, mainly because its more complicated than it has to be. Use a one bit flag:

Rich (BB code):
 SELECTROTATION equ 34h ; LSbit =1 rotates left, 0 rotates right

IRQservice:  ; used to be called MAIN
 btfsc SELECTROTATION,0 ; check flag bit
 goto LEFT_ROTATION ; flag is 1, go left

RIGHT_ROTATION:
  ; do right 20 times then
 bsf SELECTROTATION,0 ; set flag to go left
 goto IRQreturn

LEFT_ROTATION:
 ; do left 20 times then..
 bcf SELECTROTATION,0 ; clear flag to go right
 goto IRQreturn

IRQreturn:
 RETFIE
Note how the flag can only have 2 states and the motor can only run 2 directions. Nice fit. Using a full byte and inspecting its value by counting has its uses, just not here.

There are more things to do here but let's get it back on track first then clean up the rest.

Carry on.
 
Last edited:

Thread Starter

bazaic

Joined Jan 6, 2014
5
Sorry that I reply so late. I hope the code is better than the rest I posted.
Rich (BB code):
#INCLUDE P16F917.INC

MPP 			EQU 		20H
MOTORSTEPCOUNTER	EQU		24H

SELECTROTATION		EQU		34H
			

			ORG	 	0000H

RESET:		GOTO 		INIT

			ORG		0004H					;JUMP ADDRESS FROP CCP IT OCCURS WHEN TMR1H = CCPR1H AND TMR1L = CCPR1L
	
MAIN:		BTFSC		SELECTROTATION,0			;IF SELECTROTATION=0 THE MOTOR ROTATES ON THE RIGHT,ELSE THE MOTOR ROTATES ON THE LEFT												
			GOTO		LEFT_ROTATION
										;THE ROUTINE FOR RIGHT ROTATION
RIGHT_ROTATION:			
			INCF		MOTORSTEPCOUNTER,f			;INCREMENT MOTORSTEPCOUNTER AND SAVE THE CHANGE IN MOTORSTEPCOUNTER
			BCF 		PIR1,CCP1IF				;DELETE INTRERRUPT "FLAG"
			RRF 		MPP,F 					;ROTATE MPP VARIABLE THROUGH CARRY
			BCF 		MPP,7 					;PUT ON 0 THE BIT ENTERED FROM CARRY IN BIT 7 OF MPP
			BTFSC		STATUS,C				;IF CARRY=0, BIT 7 REMAINS THE SAME
			BSF			MPP,7					;ROTATE MPP WITHOUT CARRY
			MOVF		MPP,W					;MPP IS TAKEN OUT ON PORT D
			MOVWF		PORTD
			MOVF		MOTORSTEPCOUNTER,W						;PUT IN W MOTORSTEPCOUNTER
			SUBLW		14H				;SUBSTRACT: ROTATEFIXEDNUMBER-MOTORSETPCOUNTER
			BTFSS		STATUS,Z				;IF MOTORSTEPCOUNTER<ROTATEFIXEDNUMBER THEN WE RETURN TO GOTO $
			GOTO		IRQRETURN
			MOVLW		00H					;ELSE WE MAKE MOTORSTEPCOUNTER=0 AND THEN WE RETFIE TO GOTO $
			MOVWF		MOTORSTEPCOUNTER
			BSF			SELECTROTATION,0
			GOTO		IRQRETURN
									


										;THE ROUTINE FOR LEFT ROTATION
										;IT IS ALMOST THE SAME LIKE FOR THE RIGHT
LEFT_ROTATION:
			INCF		MOTORSTEPCOUNTER,f
			BCF 		PIR1,CCP1IF
			RLF 		MPP,F 
			BCF 		MPP,0 
			BTFSC		STATUS,C
			BSF			MPP,0
			MOVF		MPP,W
			MOVWF		PORTD
			MOVF		MOTORSTEPCOUNTER,W
			SUBLW		14H	
			BTFSS		STATUS,Z
			GOTO		IRQRETURN
			MOVLW		00H
			MOVWF		MOTORSTEPCOUNTER
			BCF			SELECTROTATION,0
			GOTO		IRQRETURN

IRQRETURN:
			RETFIE

			
										;INITIALIZE ROTATEFIXEDNUMBER AND MOTORSTEPCOUNTER
INIT:		
			MOVLW		02H
			MOVWF		SELECTROTATION
			MOVLW		00H
			MOVWF		MOTORSTEPCOUNTER
										;INITIALIZE MPP 
										;THE CODE BELOW I USED IT TO ROTATE THE MOTOR TO THE RIGHT SIDE AND IT WORKED;
			CLRF		STATUS
			MOVLW		88H
			MOVWF		MPP

			
PD_INI:		CLRF		PORTD
			BSF		STATUS,RP0
			MOVLW		0FH
			MOVWF		TRISD			
			BCF		STATUS,RP0
			

INTINI:		MOVLW		0C0H
			MOVWF		INTCON
			BSF		STATUS,RP0
			MOVLW		04H
			BCF		STATUS,RP0
			CLRF		PIR1
			

T1_INI:		MOVLW		0AH
			MOVWF		T1CON
			CLRF		TMR1H
			CLRF		TMR1L
			
CCPINI:		MOVLW		0BH
			MOVWF		CCP1CON
			MOVLW		20H
			MOVWF		CCPR1H
			CLRF		CCPR1L
		
			BSF		T1CON,TMR1ON
				GOTO		$
				END
 

JohnInTX

Joined Jun 26, 2012
4,787
Better! I'll go through some things and post an update. There are lots of little things that will take more time to describe than to fix, and you still can see what I mean..

In the meantime, please read section 16.3.4 CONTEXT SAVING DURING INTERRUPTS in the datasheet.
 
Last edited:

atferrari

Joined Jan 6, 2004
4,771
Hola bazaic,

I did not read your last version but, prior actually rotating the stepper, you should replace that part by flashing a LED, say red for right and green for left so you know the interrupts scheme is working. Do you actually need them?

For the test above, sure you could need a delay somewhere so you can see what is going on.

Later, I would test the rotations alone (no interrupts), separately, to make sure you are actually able in both directions.

I can not check now, how I actually did it some 15 years ago(?), but I recall, rotating a stepper by outputting a binary value, of the type maybe B'00010001' which was previously rotated left or right as needed.

Let me tell you, the whole code was extremely short / simple including LEFT, RIGHT, STOP, DISCONNECT.

One question: where do you keep track in what part of the table of sucessive values you are at every step?

I do not recall if the stepper was bipolar or unipolar. Sorry.

Code should be commented.
 

JohnInTX

Joined Jun 26, 2012
4,787
It runs under MPSIM..

I added interrupt context save/restore so you could see how its done. I also fixed up some things in init e.g. sequence of initializing interrupts etc. Most replaced code is commented out in place so you can see what I did. There is more I would recommend doing but this is a big chunk to digest. See what you think.

Rich (BB code):
    #INCLUDE P16F917.INC

    radix    hex            ; make sure we know how our numbers get evaluated
    ERRORLEVEL -.302    ; suppress bank notices

    ;******************** CONFIGURATION  ************************

;#define DEBUG_WITH_MPSIM ; uncomment this to run in MPSIM (sets TIMER1 to internal clock below)

stepsLEFT    equ    .20    ; define how many steps each way..
stepsRIGHT    equ    .20

    MESSG "Be sure to disable WDT if its not to be used"

    ;******************** BANK 0 RAM  **************************
    cblock    20h
MPP:                1        ; Motor step pattern
MOTORSTEPCOUNTER:    1        ; Counts motor steps
SELECTROTATION:        1        ; Flags: 
                            ; Bit 0: 1=Left 0=Right

    ;-------------------  INTERRUPT CONTEXT SAVE  ------------
IRQsaveSTATUS:        1        ; saves status register during IRQ
IRQsavePCLATH:        1        ; saves PCLATH during IRQ
    endc

    ;*******************  COMMON RAM  ******************
    cblock 71h                ; 70h is reserved for debuggers (PICKit, ICD etc).
IRQsaveW:            1        ; Save W regardless of IRQ bank        
    endc            


    ;****************** CODE STARTS  ****************************
        ORG         0000H

RESET:    
        clrf        PCLATH    ; paranoia strikes deep
        GOTO         INIT

    ;*********************** INTERRUPT SERVICE  *****************
        ORG        0004H    
                        ;JUMP ADDRESS FROP CCP IT OCCURS WHEN TMR1H = CCPR1H AND TMR1L = CCPR1L    
serviceIRQ:    
        ;---------------------
        ; First, save context
        movwf    IRQsaveW        ; save W in common RAM
        swapf    STATUS,W        ; get STATUS to W without changing flags
        clrf    STATUS            ; select RAM BANK 0
        movwf    IRQsaveSTATUS    ; save STATUS (its swapped)
        movf    PCLATH,W        
        movwf    IRQsavePCLATH    ; save PCLATH
        clrf    PCLATH            ; ensure ROM page 0 for IRQ routines before any jumps/calls    

        ;---------------------
        ; Verify interrupt source - deferred until later
    ;    btfss    PIR1,CCPIF        ; from CCP?
    ;    goto    svcIRQerror        ; no.. handle invalid interrupt 

        ;---------------------
        ; Service TIMER1
        bcf        PIR1,CCP1IF        ; clear flag early

        BTFSC        SELECTROTATION,0    ;IF SELECTROTATION=0 THE MOTOR ROTATES ON THE RIGHT,ELSE THE MOTOR ROTATES ON THE LEFT                                                
        GOTO        LEFT_ROTATION
        
                                        ;THE ROUTINE FOR RIGHT ROTATION
RIGHT_ROTATION:            
    ;    INCF        MOTORSTEPCOUNTER,F        ; ** not yet **INCREMENT MOTORSTEPCOUNTER AND SAVE THE CHANGE IN MOTORSTEPCOUNTER
    ;    BCF         PIR1,CCP1IF                ;DELETE INTRERRUPT "FLAG"
        RRF         MPP,F                     ;ROTATE MPP VARIABLE THROUGH CARRY
        BCF         MPP,7                     ;PUT ON 0 THE BIT ENTERED FROM CARRY IN BIT 7 OF MPP
        BTFSC        STATUS,C                ;IF CARRY=0, BIT 7 REMAINS THE SAME
        BSF            MPP,7                    ;ROTATE MPP WITHOUT CARRY
        MOVF        MPP,W                    ;MPP IS TAKEN OUT ON PORT D
        MOVWF        PORTD

        INCF        MOTORSTEPCOUNTER,F    ; keep parts of a function together
        MOVF        MOTORSTEPCOUNTER,W        ;PUT IN W MOTORSTEPCOUNTER
    ;    SUBLW        14H                        ;SUBSTRACT: ROTATEFIXEDNUMBER-MOTORSETPCOUNTER
        sublw        stepsRIGHT                ; avoid literals in the code
        BTFSS        STATUS,Z                ;IF MOTORSTEPCOUNTER<ROTATEFIXEDNUMBER THEN **DONE 
        GOTO        IRQRETURN

    ;    MOVLW        00H                        ;ELSE WE MAKE MOTORSTEPCOUNTER=0 AND THEN 
    ;    MOVWF        MOTORSTEPCOUNTER
        clrf        MOTORSTEPCOUNTER        ; better
        BSF            SELECTROTATION,0
        GOTO        IRQRETURN
                                            ;THE ROUTINE FOR LEFT ROTATION                                            
                                            ;IT IS ALMOST THE SAME LIKE FOR THE RIGHT
LEFT_ROTATION:
    ;    INCF        MOTORSTEPCOUNTER,F
    ;    BCF         PIR1,CCP1IF
        RLF         MPP,F 
        BCF         MPP,0 
        BTFSC        STATUS,C
        BSF            MPP,0
        MOVF        MPP,W
        MOVWF        PORTD

        INCF        MOTORSTEPCOUNTER,F
        MOVF        MOTORSTEPCOUNTER,W
    ;    SUBLW        14H    
        sublw        stepsLEFT
        BTFSS        STATUS,Z
        GOTO        IRQRETURN

    ;    MOVLW        00H
    ;    MOVWF        MOTORSTEPCOUNTER
        clrf        MOTORSTEPCOUNTER        ; better
        BCF            SELECTROTATION,0
        GOTO        IRQRETURN

        ;-------------------
        ; Restore context
IRQRETURN:
        clrf        STATUS            ; ensure RAM bank 0
        movf        IRQsavePCLATH,W
        movwf        PCLATH            ; restore PCLATH

        swapf        IRQsaveSTATUS,W ; restore STATUS and bank
        movwf        STATUS

        swapf        IRQsaveW,F        ; swap save W so that we can..
        swapf        IRQsaveW,W        ; swap it again into W without changing STATUS flags
                
        RETFIE                        ; return from IRQ with system restored


        ;**************** INITIALIZE SYSTEM  ***************************    

                                    ;INITIALIZE ROTATEFIXEDNUMBER AND MOTORSTEPCOUNTER
INIT:                                ; power on reset clears things but I like to enforce it
        clrf    INTCON                ; kill interupts 
        clrf    STATUS                ; select bank 0    

    ;    MOVLW    02H
    ;    MOVWF    SELECTROTATION        ; ?? - its a flag
        bcf        SELECTROTATION,0    ; init rotation direction to RIGHT

    ;    MOVLW    00H
    ;    MOVWF    MOTORSTEPCOUNTER
        clrf    MOTORSTEPCOUNTER
                                    ;INITIALIZE MPP 
    ;    CLRF    STATUS
        MOVLW    88H            ; init the motor step pattern
        MOVWF    MPP    
    
;PD_INI: <---- Dont use labels if nothing jumps to or calls it, use a comment instead
        ;----------------------------
        ; Init IO        
        CLRF    PORTD            ; init all step outputs to 0

        BSF        STATUS,RP0            
        MOVLW    0FH                ; step outputs on RD7-RD4
        MOVWF    TRISD        
        BCF        STATUS,RP0
        
;INTINI:        
    ;    MOVLW    0C0H            ; don't enable interrupts before you config the timer/ccp        
    ;    MOVWF    INTCON
    ;    BSF        STATUS,RP0      ; this is missing movwf PIE1..    
    ;    MOVLW    04H
    ;    BCF        STATUS,RP0
    ;    CLRF    PIR1
            
        ;----------------------------
        ; Init Timer 1 
;T1_INI:
    ifdef DEBUG_WITH_MPSIM
        movlw    00h                ; use 00h for debugging with MPSIM
    else    
        MOVLW    0AH                ; normal operation: not gated, 1:1 prescale, timer OFF, LP clock
    endif
                    
        MOVWF    T1CON
        CLRF    TMR1H            ; start at 0
        CLRF    TMR1L
        
        ;----------------------------
        ; Init CCP    
;CCPINI:        
        MOVLW    0BH                ; Compare mode using TMR1 w/reset
        MOVWF    CCP1CON
        MOVLW    20H                ; set CCP1 to 2000h
        MOVWF    CCPR1H
        CLRF    CCPR1L

        ;----------------------------
        ; Init Interrupts 
        bcf        PIR1,CCP1IF        ; clear the flag (timer is OFF so it will stay 0)
    
        banksel    PIE1            ; another way to select bank 1 - I don't like it but there is is..
        clrf    PIE2            ; disable unused interrupts
        movlw    B'00000100'        ; enable CCP1IF, disable others
        movwf    PIE1    
        banksel    PIR1            ; re-select bank 0
        
        bsf        INTCON,PEIE        ; enable peripheral interrupt (CCP1)
        BSF        T1CON,TMR1ON    ; start the timer

        bsf        INTCON,GIE        ; enable global IRQs LAST

MAIN:
        nop                        ; main loop :)
        nop
        GOTO        MAIN

        END
The .zip is from MPLAB 8.x and might read better in the edit window.

Have fun!
 

Attachments

Last edited:
Top