Quadrature encoder and ASM Interrupts. questions..


Closed Thread
Results 1 to 11 of 11

Hybrid View

  1. #1
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    Here's a few things I see off the bat.

    Code:
    myint 
      	; BCF INTCON,GIE ;Should I have this????
    No, you won't need that. With ASM interrupts, another interrupt can not happen until the current interrupt is finished.


    This section is for context saving. But since the PIC you're using has more than 2k of code space, PBP has already inserted the same code. So by the time it gets to your handler, those registers have been changed, and it's saving and restoring the wrong values. These should be commented out.
    Code:
    	movwf   wsave
            swapf   STATUS, W
            clrf    STATUS
            movwf   ssave
            movf    PCLATH, W
            movwf   psave
    The debounce pause is using PBP's system registers which by itself can cause the program to go wacko. Saving and restoring the system registers would help, although I think once you fix these things, you won't need that delay anyhow.
    Code:
            movlw   high 100      ; Wait 100us to debounce encoder
            movwf   R0 + 1
            movlw   low 500
            call    PAUSEUSL
    PORTB.4 and PORTB.5 will also trigger the PORTB change interrupts.
    They seem to be left floating and in input mode. Reason #3 for wacko-ness.
    Either pull them up or down with resistors, or set those 2 pins to output, and do not use them for anything else.
    <br>
    DT

  2. #2
    skimask's Avatar
    skimask Guest


    Did you find this post helpful? Yes | No

    Thumbs up Great couple of posts...

    Now those first two posts should be the PRIME EXAMPLE, a template if you will, for first time posters on the 'what/when/how/why' of how to describe a problem/issue or whatever! It's got almost everything a person would need to help troubleshoot.
    Wow!

  3. #3
    godfodder's Avatar
    godfodder Guest


    Did you find this post helpful? Yes | No

    Default

    Darrel: Thank you, I will implement your suggestions this evening and post my results.
    also brings up another question about interrupts, just to clarify since the pic I am using
    has >2k codespace, PBP does the context saving? so only on pic's with <2k I need to
    worry about saving the context? do I need to restore it still as well?

    skimask: Thanks. after numerous searches it's frustrating to run into a question like
    "how does this work.. I need code... blaa bla bla.." searching combined with
    some experimentation can get you thru most hurdles. some people just don't try
    me thinks.

  4. #4
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    > since the pic I am using has >2k codespace, PBP does the context saving? so only on pic's with <2k I need to worry about saving the context? do I need to restore it still as well?

    Correct! You only need to Save the Context on chips with 2K or less.

    The reason is because on chips with more than 2K, the Code Space is divided into several "Pages", and the PCLATH register may have to be set in order to GOTO the interrupt handler from the vector at address 0x0004. But if it changes PCLATH before the Context has been saved, then it will also change the W register that hasn't been saved yet. So PBP has to add the context saving BEFORE it jumps to the handler. (Clear as mud)

    Those registers should ALWAYS be restored at the end of the handler, no matter what size the chip is.
    <br>
    DT

  5. #5
    godfodder's Avatar
    godfodder Guest


    Did you find this post helpful? Yes | No

    Smile

    I just implemented the changes Darrel suggested and IT WORKS!!! works really well! Thank you again!

    I also solved my problem of the encoder routine counting by 4's, added a little asm in the
    beginning that checks to see if the encoder has moved, my first bit of asm I've made.. slowly
    getting a handle on things.. I also made sure to set portb<5:4> bits to outputs so they don't float.
    here is the working code if anyone needs it. the connection diagram is the same as
    my first post.

    Code:
    'Read Quadrature Encoder and display value on LCD. for pic16f876a
    
    'setup PIC Config Fuses
        @ Device pic16F876A, HS_OSC, BOD_OFF, PWRT_ON, WDT_OFF, PROTECT_OFF
    
    '************************ DEFINES HERE *************************************
    DEFINE OSC 20               ' set to 20mhz
    DEFINE LCD_DREG PORTA       ' Set Data Registers port
    DEFINE LCD_DBIT 0           ' Set starting data bit
    DEFINE LCD_RSREG PORTB      ' Set LCD RS Port
    DEFINE LCD_RSBIT 1          ' Set LCD RS Bit
    DEFINE LCD_EREG PORTB       ' Set LCD Enable Port
    DEFINE LCD_EBIT 2           ' Set LCD Enable Bit
    DEFINE LCD_BITS 4           ' Set LCD 4bit Mode
    DEFINE LCD_LINES 2          ' Set number of LCD Lines
    DEFINE LCD_COMMANDUS 2000   ' Set Command Delay time in uS
    DEFINE LCD_DATAUS 60        ' Set Data delay time in uS 
    
    clear                       'clear out variables
    
    '*********************** CHIP REGISTER SETUP *******************************
    
    ADCON0 = 7              ' turn off analog porta, set to digital IO
    ADCON1 = 7              '
    CMCON =  7              ' Turn off Port A Comparator
    TRISA = 0               ' output ports
    TRISB = %11000001       ' set input and output ports
    TRISC = 0               ' output ports
    
    '************************ PROGRAM VARIABLES HERE ***************************
    
    symbol led = portc.2        ' status led
    symbol led2 = portc.0       ' status led2
    symbol lcdbkl = portc.7     ' lcd pannel backlight
    symbol sw1 = portb.0        ' encoder switch
    
    enc_old VAR BYTE
    enc_new VAR BYTE
    enc_tmp var byte
    enc_counter VAR WORD
    enc_counter_old VAR WORD
    enc_scaler var word
    
    
    
    
    '*********************** ASSEMBLY INTERUPT VARIABLES ***********************
    wsave   var     byte $20 system
    wsave1  var     byte $a0 system  ' Necessary for devices with RAM in bank1
    wsave2  var     byte $120 system ' Necessary for devices with RAM in bank2
    wsave3  var     byte $1a0 system ' Necessary for devices with RAM in bank3
    ssave   var     byte bank0 system
    psave   var     byte bank0 system     
    
    goto start			'skip over interupt handler
    '*********************** ASSEMBLY INTERUPT HANDLER *************************
    
    define  INTHAND myint
    Asm
           
    myint 
    	; Save W, STATUS and PCLATH registers
    	; Not Necessary for Chips with >2k of Codespace
           ; movwf   wsave
           ; swapf   STATUS, W
           ; clrf    STATUS
           ; movwf   ssave
           ; movf    PCLATH, W
           ; movwf   psave  
             
    
    
    
    	;====== BEGINNING OF THE ROTARY ENCODER CODE ========
    	;The Rotary Encoder is connected to PORTB  
    	;The A signal of the encoder connected to the PIN portB.7
    	;The B signal of the encoder connected to the PIN portB.6
    	;
    	;The 4 variables used are declared in the PicBasic code.
    	;
    	;	enc_new VAR BYTE
    	;	enc_old VAR BYTE
    	;       enc_tmp VAR BYTE
    	;	enc_counter VAR WORD
    	;
    	;================================================
    		
    	   ;Read latest input from PORTB & put the value in _enc_new.
         	movf    PORTB,W
         	movwf  _enc_new
    
         	;Strip off all but the 2 MSBs in _enc_new.
         	movlw	0xc0    	     ;Create bit mask (bits 7 & 6). b'11000000' ?
         	andwf   _enc_new,F       ;Zero bits 5 thru 0.
    
            ;check to see if encoder has moved
            movf    _enc_old,W         ;move enc_old to W
            movwf   _enc_tmp           ;put W to enc_tmp
            movf    _enc_new,W         ;move enc_new to W for XOR
            xorwf   _enc_tmp,F         ;XOR enc_tmp to detect encoder movement
            btfsc   _enc_tmp,7         ;if bit is clear, encoder moved.
            goto    Continue           ;no movement exit isr
    
         	;Determine the direction of the Rotary encoder.  
         	rlf     _enc_old,F     	 ;left shift it into _enc_old to align bit 6 of 
                                     ;_enc_old with bit 7 of _enc_new.
    
         	movf    _enc_new,W     	 ;Move the contents of _enc_new to W in order to XOR.
         	xorwf   _enc_old,F    	 ;XOR previous inputs (in _enc_old) with latest
                                     ;inputs (in W) to determine CW or CCW.
     
          	btfsc   _enc_old,7     	 ;Test bit 7 of result (in _enc_old).  Skip next line
          	                         ;if it is 0 (direction is CCW).
         	goto    Up               ;Bit is 1 (direction is CW).  Go around Down
                                     ;and increment counter.
    
    Down
         	;Decrements _enc_counter because the rotary encoder moved CCW.
    	    ;Decrements _enc_counter (16 bit value), sets Z on exit.
    	     	 
            decf    _enc_counter,F      ; Decrement low byte
            incfsz  _enc_counter,W      ; Check for underflow
            incf    _enc_counter+1,F    ; Update
            decf    _enc_counter+1,F    ; Fixup
            movf    _enc_counter,W
            iorwf   _enc_counter+1,W    ; Set Z bit
    
    		
    	    ;Add here code for the CCW LED if needed.
         	bsf     _led		    ;turn on led
         	
         	goto    Continue  	    ;Branch around UP.
    
    Up
            ;Increments _enc_counter because the rotary encoder moved CW.
            ;Increments _enc_counter (16 bit value), sets Z on exit.
    
            incfsz  _enc_counter,W      ; Add one to low byte
            decf    _enc_counter+1,F    ; No carry (negates next step)
            incf    _enc_counter+1,F    ; Add one to high byte
            movwf   _enc_counter        ; Store updated low byte back.
            iorwf   _enc_counter+1,W    ; Set Z flag
    
    		
    	   ;Add here code for the CW LED if needed.
    	   bsf     _led2		    ;turn on led
    	
    Continue     	
         	;Assign the latest encoder inputs (in _enc_new) to _enc_old.
         	movf 	_enc_new,W
         	movwf   _enc_old
    
            ; Restore saved registers
            movf    psave, W
            movwf   PCLATH
            swapf   ssave, W
            movwf   STATUS
            swapf   wsave, F
            swapf   wsave, W
            bcf     INTCON, RBIF		  ; Clear the Interupt Flag
            RETFIE                 	  	  ; Return from interrupt
    endasm
    
    '***************************************************************************
    '************************* PROGRAM STARTS HERE *****************************
    '***************************************************************************
    
    START:              ' Main Program starts here
    
    LCDOUT $FE,1		'Init The LCD
    pause 500		    'wait for LCD to start 
    
    lcdout $FE,1,"ENCODER"	    'display splash screen
    LCDOUT $FE,$C0," TEST "       
    high lcdbkl                 ' turn on backlight
    
    
    '************************** SET DEFAULT SETTINGS HERE **********************
    pause 2000		    ' just wait a bit to read splash screen
    enc_counter = 1024	' set default encoder value
    enc_counter_old = 1024	
    
    lcdout $FE,1,"ENCODER"  ' change display
    lcdout $fe,$C0, DEC5 enc_counter
    INTCON = %10001000      ' Enable PortB Change Interupts  
    
    
    '************************** TESTING ROUTINES HERE **************************
    test:
                if  enc_counter <> enc_counter_old then 'see if value has changed
                    enc_counter_old = enc_counter		'move new value to old
                    lcdout $fe,$C0, DEC5 enc_counter	'display enc_counter value
                    low led		                        'turn off CCW led
                    low led2	                        'turn off CW led
                endif
    goto test

  6. #6
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    WooHoo! We're on a roll.

    People maken stuff happen ... I love it!

    Stick around godfodder.
    <br>
    DT

  7. #7


    Did you find this post helpful? Yes | No

    Default

    After trying your code I got the following compiler error ?

    Warnin[207] C:\pbp..... Filename.asm 81 : Found label after column 1. (Device)
    Error[122] C:\pbp........ Filename.asm 81 : Illegal opcode (Pic16F876A)

    Any idea ?

    PBP2.50 and MPASM

  8. #8


    Did you find this post helpful? Yes | No

    Default Re: Quadrature encoder and ASM Interrupts. questions..

    Quote Originally Posted by godfodder View Post
    I just implemented the changes Darrel suggested and IT WORKS!!! works really well! Thank you again!

    I also solved my problem of the encoder routine counting by 4's, added a little asm in the
    beginning that checks to see if the encoder has moved, my first bit of asm I've made.. slowly
    getting a handle on things.. I also made sure to set portb<5:4> bits to outputs so they don't float.
    here is the working code if anyone needs it. the connection diagram is the same as
    my first post.

    Code:
    'Read Quadrature Encoder and display value on LCD. for pic16f876a
    
    'setup PIC Config Fuses
        @ Device pic16F876A, HS_OSC, BOD_OFF, PWRT_ON, WDT_OFF, PROTECT_OFF
    
    '************************ DEFINES HERE *************************************
    DEFINE OSC 20               ' set to 20mhz
    DEFINE LCD_DREG PORTA       ' Set Data Registers port
    DEFINE LCD_DBIT 0           ' Set starting data bit
    DEFINE LCD_RSREG PORTB      ' Set LCD RS Port
    DEFINE LCD_RSBIT 1          ' Set LCD RS Bit
    DEFINE LCD_EREG PORTB       ' Set LCD Enable Port
    DEFINE LCD_EBIT 2           ' Set LCD Enable Bit
    DEFINE LCD_BITS 4           ' Set LCD 4bit Mode
    DEFINE LCD_LINES 2          ' Set number of LCD Lines
    DEFINE LCD_COMMANDUS 2000   ' Set Command Delay time in uS
    DEFINE LCD_DATAUS 60        ' Set Data delay time in uS 
    
    clear                       'clear out variables
    
    '*********************** CHIP REGISTER SETUP *******************************
    
    ADCON0 = 7              ' turn off analog porta, set to digital IO
    ADCON1 = 7              '
    CMCON =  7              ' Turn off Port A Comparator
    TRISA = 0               ' output ports
    TRISB = %11000001       ' set input and output ports
    TRISC = 0               ' output ports
    
    '************************ PROGRAM VARIABLES HERE ***************************
    
    symbol led = portc.2        ' status led
    symbol led2 = portc.0       ' status led2
    symbol lcdbkl = portc.7     ' lcd pannel backlight
    symbol sw1 = portb.0        ' encoder switch
    
    enc_old VAR BYTE
    enc_new VAR BYTE
    enc_tmp var byte
    enc_counter VAR WORD
    enc_counter_old VAR WORD
    enc_scaler var word
    
    
    
    
    '*********************** ASSEMBLY INTERUPT VARIABLES ***********************
    wsave   var     byte $20 system
    wsave1  var     byte $a0 system  ' Necessary for devices with RAM in bank1
    wsave2  var     byte $120 system ' Necessary for devices with RAM in bank2
    wsave3  var     byte $1a0 system ' Necessary for devices with RAM in bank3
    ssave   var     byte bank0 system
    psave   var     byte bank0 system     
    
    goto start			'skip over interupt handler
    '*********************** ASSEMBLY INTERUPT HANDLER *************************
    
    define  INTHAND myint
    Asm
           
    myint 
    	; Save W, STATUS and PCLATH registers
    	; Not Necessary for Chips with >2k of Codespace
           ; movwf   wsave
           ; swapf   STATUS, W
           ; clrf    STATUS
           ; movwf   ssave
           ; movf    PCLATH, W
           ; movwf   psave  
             
    
    
    
    	;====== BEGINNING OF THE ROTARY ENCODER CODE ========
    	;The Rotary Encoder is connected to PORTB  
    	;The A signal of the encoder connected to the PIN portB.7
    	;The B signal of the encoder connected to the PIN portB.6
    	;
    	;The 4 variables used are declared in the PicBasic code.
    	;
    	;	enc_new VAR BYTE
    	;	enc_old VAR BYTE
    	;       enc_tmp VAR BYTE
    	;	enc_counter VAR WORD
    	;
    	;================================================
    		
    	   ;Read latest input from PORTB & put the value in _enc_new.
         	movf    PORTB,W
         	movwf  _enc_new
    
         	;Strip off all but the 2 MSBs in _enc_new.
         	movlw	0xc0    	     ;Create bit mask (bits 7 & 6). b'11000000' ?
         	andwf   _enc_new,F       ;Zero bits 5 thru 0.
    
            ;check to see if encoder has moved
            movf    _enc_old,W         ;move enc_old to W
            movwf   _enc_tmp           ;put W to enc_tmp
            movf    _enc_new,W         ;move enc_new to W for XOR
            xorwf   _enc_tmp,F         ;XOR enc_tmp to detect encoder movement
            btfsc   _enc_tmp,7         ;if bit is clear, encoder moved.
            goto    Continue           ;no movement exit isr
    
         	;Determine the direction of the Rotary encoder.  
         	rlf     _enc_old,F     	 ;left shift it into _enc_old to align bit 6 of 
                                     ;_enc_old with bit 7 of _enc_new.
    
         	movf    _enc_new,W     	 ;Move the contents of _enc_new to W in order to XOR.
         	xorwf   _enc_old,F    	 ;XOR previous inputs (in _enc_old) with latest
                                     ;inputs (in W) to determine CW or CCW.
     
          	btfsc   _enc_old,7     	 ;Test bit 7 of result (in _enc_old).  Skip next line
          	                         ;if it is 0 (direction is CCW).
         	goto    Up               ;Bit is 1 (direction is CW).  Go around Down
                                     ;and increment counter.
    
    Down
         	;Decrements _enc_counter because the rotary encoder moved CCW.
    	    ;Decrements _enc_counter (16 bit value), sets Z on exit.
    	     	 
            decf    _enc_counter,F      ; Decrement low byte
            incfsz  _enc_counter,W      ; Check for underflow
            incf    _enc_counter+1,F    ; Update
            decf    _enc_counter+1,F    ; Fixup
            movf    _enc_counter,W
            iorwf   _enc_counter+1,W    ; Set Z bit
    
    		
    	    ;Add here code for the CCW LED if needed.
         	bsf     _led		    ;turn on led
         	
         	goto    Continue  	    ;Branch around UP.
    
    Up
            ;Increments _enc_counter because the rotary encoder moved CW.
            ;Increments _enc_counter (16 bit value), sets Z on exit.
    
            incfsz  _enc_counter,W      ; Add one to low byte
            decf    _enc_counter+1,F    ; No carry (negates next step)
            incf    _enc_counter+1,F    ; Add one to high byte
            movwf   _enc_counter        ; Store updated low byte back.
            iorwf   _enc_counter+1,W    ; Set Z flag
    
    		
    	   ;Add here code for the CW LED if needed.
    	   bsf     _led2		    ;turn on led
    	
    Continue     	
         	;Assign the latest encoder inputs (in _enc_new) to _enc_old.
         	movf 	_enc_new,W
         	movwf   _enc_old
    
            ; Restore saved registers
            movf    psave, W
            movwf   PCLATH
            swapf   ssave, W
            movwf   STATUS
            swapf   wsave, F
            swapf   wsave, W
            bcf     INTCON, RBIF		  ; Clear the Interupt Flag
            RETFIE                 	  	  ; Return from interrupt
    endasm
    
    '***************************************************************************
    '************************* PROGRAM STARTS HERE *****************************
    '***************************************************************************
    
    START:              ' Main Program starts here
    
    LCDOUT $FE,1		'Init The LCD
    pause 500		    'wait for LCD to start 
    
    lcdout $FE,1,"ENCODER"	    'display splash screen
    LCDOUT $FE,$C0," TEST "       
    high lcdbkl                 ' turn on backlight
    
    
    '************************** SET DEFAULT SETTINGS HERE **********************
    pause 2000		    ' just wait a bit to read splash screen
    enc_counter = 1024	' set default encoder value
    enc_counter_old = 1024	
    
    lcdout $FE,1,"ENCODER"  ' change display
    lcdout $fe,$C0, DEC5 enc_counter
    INTCON = %10001000      ' Enable PortB Change Interupts  
    
    
    '************************** TESTING ROUTINES HERE **************************
    test:
                if  enc_counter <> enc_counter_old then 'see if value has changed
                    enc_counter_old = enc_counter		'move new value to old
                    lcdout $fe,$C0, DEC5 enc_counter	'display enc_counter value
                    low led		                        'turn off CCW led
                    low led2	                        'turn off CW led
                endif
    goto test
    Re-opening this thread because I'm having trouble with my rotary encoder ISR routine also seemingly happening 4 times per detent. I thought I had a similar check on whether anything changed in my PBP ISR but for some reason MotorRPM is always incrementing by 4. Any ideas?

    Code:
    ' ***************************************************************
    ' [IOC - interrupt handler]
    ' ***************************************************************
    RotEncAdjust:
        New_Bits = PORTA & (%00000011)
    
        IF New_Bits = Old_Bits Then DoneRotEnc
    
        RotEncDir = New_Bits.1 ^ Old_Bits.0
        IF RotEncDir = 1 Then
            ; CW rotation - increase speed but only to a max of 'MaxDuty'
            IF MotorRPM < MaxDuty then MotorRPM = MotorRPM + 1
        Else
            ' CCW rotation - decrease speed to a min of 0
            IF MotorRPM > 0 Then MotorRPM = MotorRPM - 1
        EndIF
    
    DoneRotEnc:
        Old_Bits = New_Bits
    
        IOCAF.0 = 0                     ' Clear interrupt flags
        IOCAF.1 = 0
    
    @ INT_RETURN
    (A/B outputs of rotary encoder connected to PORTA.1/0)

Similar Threads

  1. Instant Int and encoder (Elect. Gearing)
    By boroko in forum mel PIC BASIC Pro
    Replies: 13
    Last Post: - 29th November 2009, 02:29
  2. Probe to read quadrature encoder with DT_INT need help
    By phoenix_1 in forum mel PIC BASIC Pro
    Replies: 11
    Last Post: - 31st August 2009, 20:43

Members who have read this thread : 0

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts