decoding quadrature encoders


Closed Thread
Results 1 to 40 of 94

Hybrid View

  1. #1
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695


    Did you find this post helpful? Yes | No

    Default

    Hi,

    Below is one part of the interrupt assembly code.
    (I have tested the code with the MPLAB simulator).

    This is not the end of the difficulties.

    I can help (the forum can help) if you do the following:

    Write a PicBasic program that displays on the LCD the
    rotary encoder count. The program will use an
    interrupt-onchange in assembly for the encoder.
    (Please write a new well formatted program).

    The assembly code I have posted today is only one
    part of the code you will need in the interrupt routine.
    BEFORE my code you will have to save the processor context.
    AFTER my code you will have to restore the processor context.
    See PicBasic manual '9.3. Interrupts in Assembler' for
    everything else you will need in order to use an interrupt
    in assembly.

    For now do not modify my code. Add everything, also the
    two comment lines. (Cut & Paste).
    (;====== BEGINNING OF THE ROTARY ENCODER CODE ========)
    (;========= END OF THE ROTARY ENCODER CODE ===========)

    If you like you can add the code for the two LEDs CCW and CW.
    See comments in the code for the LEDs.

    In my code I use 3 variables that you will have to declare
    in your PicBasic program. These variables have new names.
    (Not the same names you have used in your code).
    I don't like names like 'new' because 'new' can be a
    reserved word for some compiler.

    The 3 variables are:

    enc_new VAR BYTE
    enc_old VAR BYTE
    enc_counter VAR WORD

    Set to 0 (zero) the variable enc_old when you power up
    the PIC, before you enter the main loop of your PicBasic
    program. This is not necessary but it will help you to
    understand how this variable is used.


    * * *

    From your post of the 6th May 2005, 12:06

    ....im using a 16F873A,which has a 4K flash,meaning 4 banks...
    according to the PBP manual, i have to define wsave,wsave1,wsave2,wsave3...
    it takes in wsave and wsave1...but when i declare wsave2 and wsave3..it gives a RAM_END 255 error


    From the PicBasic manual
    2.2. Your First Program

    If you don’t tell it otherwise, the PicBasic Pro Compiler defaults to
    creating code for the PIC16F84. To compile code for PICmicro MCUs
    other than the PIC16F84, simply use the -P command line option
    described later in the manual to specify a different target processor. For
    example, if you intend to run the above program, BLINK.BAS, on a
    PIC16F877, compile it using the command:

    PBP -p16f877 blink

    So verify what you need for the 16F873A.

    Luciano

    Code:
    	;====== 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	B'11000000'	;Create bit mask (bits 7 & 6).
         	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.
         	
         	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.
    	
    Continue 
         	
         	;Assign the latest encoder inputs (in _enc_new) to _enc_old.
    
         	movf 	_enc_new,W
         	movwf   _enc_old
    
    	;============ END OF THE ROTARY ENCODER CODE =====
    Last edited by Luciano; - 12th May 2005 at 22:20.

  2. #2
    ice's Avatar
    ice Guest


    Did you find this post helpful? Yes | No

    Thumbs up Working!!

    Thank you so much Luciano...

    The code is..
    working good ,
    direction decoding ok
    counting on LCD[no garbage,dispays properly]


    A few questions
    1>You never clear the Int on PortB change flag in the interrupt.is it because your reading from portB,which is one of the ways to clear the flag.

    2>retfie.. Neither is the retfie used to return from where the program left.

    3>why didnt my program work ,when i used the INT0 interrupt.I cant figure it out.


    4>I use CodeDesigner Lite as an IDE.I get t0 select the chip from a drop down menu.I select 16f873a.I even put the line PBP -p16F873A in the command line option,but the error on using wsave2 and wsave3,still persists.


    Working with enc_counter on 16 bits is a bit difficult to work on the current project.I need 32 bits.so back to my code.

  3. #3
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695


    Did you find this post helpful? Yes | No

    Default

    ====================================

    Question 1


    (From data sheet PIC16F87XA).

    INTCON REGITER

    bit 0 RBIF: RB Port Change Interrupt Flag bit

    1 = At least one of the RB7:RB4 pins changed state; a mismatch condition will continue to set
    the bit. Reading PORTB will end the mismatch condition and allow the bit to be cleared
    (must be cleared in software).

    0 = None of the RB7:RB4 pins have changed state

    * * *

    Yes, you must clear RBIF in software.


    ======================================

    Question 2

    Yes, you need 'retfie' before 'endasm'.
    Just do what is in the PicBasic Pro manual.

    ======================================

    Question 3

    The encoder outputs that in CW direction:

    Step 1 A=0 B=0
    Step 2 A=1 B=0
    Step 3 A=1 B=1
    Step 4 A=0 B=1

    If you use the interrupt INT0, you can monitor
    only one signal of the encoder. If you monitor
    the encoder signal A with the PIN RB0 (INT0) the
    interrupt is raised when the encoder goes from
    step 1 to step 2. (0 to 1).
    From step 2 to step 3 the interrupt is not raised
    because the encoder signal A is '1' in step 2 and
    also '1' in step 3.

    ======================================

    Question 4

    You will have to fix that. This is a ticking bomb.
    I don't use PicBasic. I cannot answer this question.
    Do what I told you in my last post and ask the forum
    for help.

    ======================================

    From your last post:
    I need 32 bits.so back to my code.

    * * *

    The PicBasic Pro compiler only supports a maximum
    variable size of 16 bits.

    What kind of 32-bit operations do you need?

    Best regards,

    Luciano

  4. #4
    ice's Avatar
    ice Guest


    Did you find this post helpful? Yes | No

    Default

    Luciano,
    Thanks for answering the questions

    when i use "retfie" to return to the main program from where it left..the PIC "freezes" and both the LEDs light up.
    It's all good when i dont use retfie

    I can count only from 0 to 65535 with 16bits..which is not enough.hence i need 32 bit addition.

  5. #5
    mytekcontrols's Avatar
    mytekcontrols Guest


    Did you find this post helpful? Yes | No

    Lightbulb Here is another Encoder/Counter example

    ICE
    I may have another solution for you (or then again not). Here is a link to something I have been working on which utilizes a timer interrupt to sample an encoder, and then increment or decrement a 6 digit counter (includes prescaler). Since everything to do with sampling and updating are via the assembly interrupt there is no danger of missing pulses. Also picking the minimum sampling frequency necessary for a particular application tends to add additional debounce to the encoder inputs. And last but not least, using the byte per digit counter approach allows for any counter length desired (not limited to a 16 bit word to hold your value).

    Check it out:https://www.picbasic.co.uk/forum/showthread.php?t=1886
    (Scroll down towards the bottom)

  6. #6
    jmen's Avatar
    jmen Guest


    Did you find this post helpful? Yes | No

    Default quadrature decoding with a 18F452

    Hello ICE, hi Luciano, hello everyone.
    I try to use an PIC 18F452 with the Code posted from ICE and Luciano. The Code I have posted does not work and I dont understand wy but to tell the truth: I never tried to use Assembler and I dont have experience with Interrupts so maybe I´v done totaly... but maybe someone can help me?!

    The Quad-Encoder is connected to portB.6 and 7.
    Is the way I saved and restored the Registers right?
    At the Assembler Code I changed the "rlf" statement to "rlcf" is this OK (I think the 18F452 do not support rlf)?
    Is the declaration of the variables for the assembler code right?

    Sorry for the spacing at the code, but I dont know how to remove it... :-(

    Thanks in advance,
    Jan



    <div class="smallfont" style="margin-bottom:2px">Code:</div>
    <pre style="margin:0px; padding:2px; border:1px inset; width:640px; height:434px; overflow:auto">DEFINE OSC 40
    'hserout requires an rs-232 driver
    'Set transmit register to transmitter enabled (normal value "20h" / highSpeed "24h")
    DEFINE HSER_TXSTA 24h
    'Set receive register to to receiver enabled
    DEFINE HSER_RCSTA 90h
    'Set baud rate
    DEFINE HSER_BAUD 19200

    '---VARIABLES required for the ASSAMBLER code
    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

    enc_old VAR BYTE
    enc_new VAR BYTE

    enc_counter VAR WORD

    '---Other Vriables

    enc_counter_old VAR WORD
    maxcount con 1800
    startcount con 1000

    led0 VAR PORTA.0 'leds for showing the direction
    led1 VAR PORTA.1
    led2 VAR PORTA.2

    enc_counter = startcount

    TRISA = %00000000 ' Makes the pins of PortA output
    TRISB = %11111111 ' Makes the pins of PortB input


    GOTO start
    ' Define interrupt handler
    define INTHAND myint
    ' Assembly language interrupt handler
    Asm
    ; Save W, STATUS and PCLATH registers, if not done previously
    myint 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 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 B'11000000' ;Create bit mask (bits 7 & 6).
    andwf _enc_new,F ;Zero bits 5 thru 0.

    ;Determine the direction of the Rotary encoder.
    rlcf _enc_old,F ;was rlf - 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

    ;code for the CCW LED.
    bcf _led0 ; If interrupt, turn off LED
    goto Continue ;Branch around UP.
    Up
    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

    ;code for the CW LED.
    bcf _led1 ; If interrupt, turn off 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

    retfie ; Return from interrupt
    endasm

    start:
    INTCON = %10001000 ' Enable INTERRUPT "on change" ON PORTB and clears FLAGS

    pause 30
    portA = %11111111
    HSEROUT [#enc_counter,10,13] 'only for debugging

    loop:

    if enc_counter_old <> enc_counter then 'looks if counter has changed
    HSEROUT [#enc_counter,10,13]
    enc_counter_old = enc_counter
    endif

    goto loop
    END
    </pre>
    Last edited by jmen; - 4th August 2005 at 08:05.

  7. #7
    Join Date
    Aug 2005
    Posts
    4


    Did you find this post helpful? Yes | No

    Default

    hello everyone!

    i'm dealing with a similiar problem this weekend.

    i believe that it might be better to solve some of
    the functions via hardware, using one D-type flip-flop
    and one AND logical gate.

    if you connect A to D input of FF and B to a
    raising edge clock, the output (Q) would
    indicate the direction of movement, it would
    be high when A goes high before B and low
    when B goes high before A. output of FF would
    be connected to B.3 (i want to use 16f628 with
    B.1 and B.2 as hardware UART).

    then i'd lead A and B through AND gate and connect
    its output to B.0/INT (interrupts enabled)

    counter would then be changed (incremented
    or decremented, according to B.3) whenever
    INT occours.

    ICE, can you please please send me complete
    working code in PBP?

  8. #8
    mytekcontrols's Avatar
    mytekcontrols Guest


    Did you find this post helpful? Yes | No

    Default

    I don't know if this will help or not, but I made some changes to some earlier code I posted which was aimed at the 18F series. Besides there being a few flaws with the syntax of the rotate function in the original code, I just didn't like some aspects of it.

    So here is an example of what I am now using to check an encoder's direction and movement (within Interrupt Service Routine):
    Code:
    chk_encoder
    ;Read latest input from ENCODER & put the value in NEW.
        clrf    _newenc         ; clear "new" encoder bit pair storage,
        btfsc   _ENCA           ; and transfer state of encoder bits.
        bsf     _newenc,0
        btfsc   _ENCB
        bsf     _newenc,1
    
    ;Compare previous encoder inputs (OLD) with latest ones (NEW).
    
        movf _newenc,W		;Move the contents of NEW to TEMP and OLD to W
       movwf   _tmpenc      	;in order to compare them with XOR.
       movf    _oldenc,W     
       xorwf   _tmpenc,F    	;XOR previous inputs (in W) with latest inputs 
                             	;(in TEMP) to see if they are equal.
       btfsc   STATUS,Z        ;Test result and skip next line if zero flag clear.
    
        goto    restore      	;Result = zero.  Previous inputs equal latest 
                             	;inputs.  Rotary encoder did not move. Return
                             	;from interrupt.
    
    ;Result is a non-zero value.  Rotary encoder moved.  Determine direction.  
    
        bsf     _ENCDRnew       ; flag that encoder value has changed
        bcf	STATUS,C            ;Clear the carry bit in the status register and
       rlcf     _oldenc,F     	;left shift it into OLD to align bit 1 of OLD
                             	;with bit 0 of NEW.
       movf    _newenc,W     	;Move the contents of NEW to W in order to XOR.
       xorwf   _oldenc,F    	;XOR previous inputs (in OLD) with latest
                             	;inputs (in W) to determine CW or CCW.
       btfsc   _oldenc,1     	;Test bit 1 of result (in OLD).  Skip next line
                             	;if it is 0 (direction is CCW).
       goto    encoder_up      ;Bit is 1 (direction is CW).  Go around Down
                             	;and increment counter.
    encoder_down
        bcf     _direnc         ; clear direction bit = CCW (count down)
        goto    countdown
    
    encoder_up
        bsf     _direnc         ; set direction bit = CW (count up)
    ;drops into countup part of routine
    In this first section:
    Code:
    ;Read latest input from ENCODER & put the value in NEW.
        clrf    _newenc         ; clear "new" encoder bit pair storage,
        btfsc   _ENCA           ; and transfer state of encoder bits.
        bsf     _newenc,0
        btfsc   _ENCB
        bsf     _newenc,1
    We are using aliases for the actual encoder "A" and "B" inputs. By doing this , it makes your code more transportable to different devices and/or board configurations.

    These aliases would have been earlier equated as follows:
    Code:
    ENCA    VAR PORTC.4 ; any port, any bit
    ENCB    VAR PORTC.5 ; any port, any bit
    Also if your counter is going backwards from the desired direction, all you have to do is swap the previous assignments:
    Code:
    ENCB    VAR PORTC.4 ; any port, any bit
    ENCA    VAR PORTC.5 ; any port, any bit
    And here is the code that determines if the encoder has moved or not:
    Code:
    ;Compare previous encoder inputs (OLD) with latest ones (NEW).
    
        movf _newenc,W		;Move the contents of NEW to TEMP and OLD to W
       movwf   _tmpenc      	;in order to compare them with XOR.
       movf    _oldenc,W     
       xorwf   _tmpenc,F    	;XOR previous inputs (in W) with latest inputs 
                             	;(in TEMP) to see if they are equal.
       btfsc   STATUS,Z        ;Test result and skip next line if zero flag clear.
    
        goto    restore      	;Result = zero.  Previous inputs equal latest 
                             	;inputs.  Rotary encoder did not move. Return
                             	;from interrupt.
    Here is where we determine the encoder's direction of travel:
    Code:
    ;Result is a non-zero value.  Rotary encoder moved.  Determine direction.  
    
        bsf     _ENCDRnew       ; flag that encoder value has changed
        bcf	STATUS,C            ;Clear the carry bit in the status register and
       rlcf     _oldenc,F     	;left shift it into OLD to align bit 1 of OLD
                             	;with bit 0 of NEW.
       movf    _newenc,W     	;Move the contents of NEW to W in order to XOR.
       xorwf   _oldenc,F    	;XOR previous inputs (in OLD) with latest
                             	;inputs (in W) to determine CW or CCW.
       btfsc   _oldenc,1     	;Test bit 1 of result (in OLD).  Skip next line
                             	;if it is 0 (direction is CCW).
       goto    encoder_up      ;Bit is 1 (direction is CW).  Go around Down
                             	;and increment counter.
    Besides determining which counter routine to execute (countup or countdown), a direction bit (direnc) is also assigned that can be accessed by the main PBP program and used to determine, or display the last known direction of the encoder.
    Code:
    encoder_down
        bcf     _direnc         ; clear direction bit = CCW (count down)
        goto    countdown
    
    encoder_up
        bsf     _direnc         ; set direction bit = CW (count up)
    ;drops into countup part of routine
    Lastly, be sure to include this as part of your ISR "restore" code:
    Code:
    restore
       	movf 	_newenc,W       ; update encoder OLD to equal NEW
       	movwf   _oldenc
    In order to determine if an encoder change has occured within your PBP program just check and then clear the ENCDRnew bit.

    And here are the equates for some of the variables I am using:
    Code:
    newenc var byte
    oldenc var byte
    tmpenc var byte
    direnc var bit
    ENCDRnew var bit
    I hope this helps and doesn't just confuse the situation,
    Last edited by mytekcontrols; - 29th October 2005 at 15:19.

Similar Threads

  1. Quick thoughts: DT Ints with encoders
    By kevlar129bp in forum mel PIC BASIC Pro
    Replies: 19
    Last Post: - 7th January 2010, 01:01
  2. PMDC SERVO MOTOR WITH quadrature encoder DRIVE ?
    By phoenix_1 in forum Schematics
    Replies: 37
    Last Post: - 22nd November 2009, 19:45
  3. 32-bit Quadrature Counter With Serial Interface
    By precision in forum mel PIC BASIC Pro
    Replies: 4
    Last Post: - 10th June 2008, 02:49
  4. quad encoders
    By cpayne in forum mel PIC BASIC Pro
    Replies: 0
    Last Post: - 13th March 2007, 17:49
  5. "momentary" IR decoding
    By sporker in forum mel PIC BASIC Pro
    Replies: 0
    Last Post: - 20th June 2005, 01:53

Members who have read this thread : 4

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