Quadrature encoder and ASM Interrupts. questions..


Closed Thread
Results 1 to 11 of 11
  1. #1
    godfodder's Avatar
    godfodder Guest

    Question Quadrature encoder and ASM Interrupts. questions..

    Hi all.
    I've been lurking around here for about a month now learning my way to getting up to speed on pic programming, I have done numerous basicX and stamp projects in the past, but since discovering the speed and ability of direct programming, the time of stamps is in the way past now. Want to say it's amazing how helpful these forums are, armed with the power of search
    there is no reason why anyone can't be up and running in a flash...

    So that aside here's my first roadblock, I would like to get up to speed in using asm interrupts
    decided to make a project that utilizes a rotary quadrature encoder, (panel type) it's not going to be machine driven, just for human interaction. after searching and discovering numerous threads about multiple ways to read the encoder, I wanted to try what looks like an elegant method of reading the port change interrupt on RB<7:4> to change a variable in the background letting me just check that variable in the main program loop, to display on a basic parallel lcd. so I will first describe my setup/pic/ect.. then explain what issues I am experiencing. Thanks in advance for any help or insight..

    I am using a PIC16F876A

    Here is my hardware wiring.

    ( 1) MCLR --> 4.7k --> 5v+
    ( 2) RA0 --> LCD Data
    ( 3) RA1 --> LCD Data
    ( 4) RA2 --> LCD Data
    ( 5) RA3 --> LCD Data
    ( 6) RA4 -->
    ( 7) RA5 -->
    ( 8) Vss --> Gnd
    ( 9) OSC1 --> 20mhz Osc
    (10) OSC2 --> 20mhz Osc
    (11) RC0 --> Status Led1
    (12) RC1 -->
    (13) RC2 --> Status Led2
    (14) RC3 -->
    (15) RC4 -->
    (16) RC5 -->
    (17) RC6 -->
    (18) RC7 --> LCD Backlight
    (19) Vss --> Gnd
    (20) Vdd --> 5v+ --> .1uf Cap --> Gnd
    (21) RB0 --> Encoder PushButton --> 10k --> 5v+
    (22) RB1 --> LCD RS Bit
    (23) RB2 --> LCD Enable Bit
    (24) RB3 -->
    (25) RB4 -->
    (26) RB5 -->
    (27) RB6 --> Encoder Phase A --> 10k --> 5v+
    (28) RB7 --> Encoder Phase B --> 10k --> 5v+

    I have been reading thru the following threads to learn as much as I can about using
    various ways to read an encoder.

    http://www.picbasic.co.uk/forum/showthread.php?t=1552
    Ive based my ISR on Lucianos code from the above thread. (Thanks!!!)

    also have looked at using timer interrupts for encoder reading
    http://www.picbasic.co.uk/forum/showthread.php?t=1886
    I have plans later for the timer, so going to try to stick with portB change for now. (baby steps)

    I've also found some other code (non asm, just picbasic) that does a pretty good job
    can't seem to find the post at the moment.. but it had a real wierd issue, going one direction
    I would miss every 3rd step, but the other way it was perfect..
    I know I don't really need the precision that an asm interrupt has for a slow turning encoder,
    but would also like to use this oppertunity to learn how to do it efficiently.

    so first here is my code...
    Code:
    'problem code
    
    '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
    ADCON1 = 7              ' turn off analog porta, set to digital IO
    CMCON =  7              ' Turn off Port A Comparator
    TRISA = 0               ' output ports
    TRISB = %11111001       ' 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_counter VAR WORD
    enc_counter_old 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 
      	; BCF INTCON,GIE ;Should I have this????
    	; Save W, STATUS and PCLATH registers, if not done previously
    	movwf   wsave
            swapf   STATUS, W
            clrf    STATUS
            movwf   ssave
            movf    PCLATH, W
            movwf   psave  
             
            movlw   high 100      ; Wait 100us to debounce encoder
            movwf   R0 + 1
            movlw   low 500
            call    PAUSEUSL
    
    
    	;====== 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 3 variables used are declared in the PicBasic code.
    	;
    	;	enc_new VAR BYTE
    	;	enc_old 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.
    
         	;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
    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
    ok, questions I have.. [oops post is getting too long... see next post]

  2. #2
    godfodder's Avatar
    godfodder Guest


    Did you find this post helpful? Yes | No

    Default

    1) have I missed anything as far as configuration goes?, I've just got a handle on reading and configuring things from the microchip datasheets, still have a ways to go.. so probably missed something stupid.

    2) When entering/exiting the asm interrupt I am saving and restoring the processor context, but is there any other things I should be saving/restoring? also should I disable the interrupt once first entered? can an interrupt be triggered while within the interrupt itself?


    3) The routine executes everytime a condition changes on the interrupt pins, yet there is no provision in the code to exit if the values didn't change... um ok leme explain that one better. every "indent click" of the encoder the routine gets triggered 4 times (twice for each phase), each time the routine needs to exit with an Up/Down result.. shouldn't there be a exit if nothing changed? this is where i'm a bit fuzzy, I am including a few screenshots bellow of a logic analyzer capture to illustrate this better I hope. basically I get increments of 4 on the counter every click of the encoder.

    4) I've added some debounce asm to the routine which seems to work. just want to double
    check it is ok way to do it.


    5) now my biggest issue which is really confusing. every few turns of the encoder, about a second after, the pic freaks out and triggers the interrupt routine what looks like 100-1000 times. scrambling the counter, and sometimes causing some corruption on the lcd. I've included the logic analyzer capture bellow. What could be causing this??

    oh, forgot to mention I am using "Panasonic EVQ-WTEF2515B" rotary encoder.
    here is what I managed to find as far as a datasheet.

    I have used a led on/off to be able to capture when the interrupt is tiggered by my
    logic analyzer.

    Screen Shot of Logic Analyzer for Multiple Interrupts per indent


    Screen Shot of Logic Analyzer for Pic freakout..


    Screen Shot of Logic Analyzer Closeup of Pic freakout..


    Thanks again for any help, and looking forward to be able to contribute to the community.

  3. #3
    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

  4. #4
    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!

  5. #5
    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.

  6. #6
    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

  7. #7
    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

  8. #8
    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

  9. #9


    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

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


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by RFsolution View Post
    Any idea ?
    DEVICE statements are for the PM assembler.

    You could compile it with PM, or change them to __config's for MPASM.
    <br>
    DT

  11. #11


    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, 03: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, 21:43

Members who have read this thread : 2

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