decoding quadrature encoders


Closed Thread
Results 1 to 40 of 94

Hybrid View

  1. #1
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,621


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    So you're getting ~360 counts for 180 degrees movement. Then the interrupt code must be doing 2x decoding instead of 4x - provided the encoder is actually 360cpr.
    You loose the negative value because PBP doesn't handle the multiplication properly and you're of course better off copying the enc_counter variable to a second one for scaling and display purposes, sorry about all that.

    Try this (untested):

    Code:
    Degrees VAR WORD
    Sign VAR BIT
    
    Degrees = enc_counter   ' Degrees is now -180 to 180
    Sign = Degrees.15   ' Save sign
    Degrees = (ABS Degrees) * 5   ' Degrees is now 0 to 900
    If Sign THEN Degrees = -Degrees   ' Restore sign, value is now -900 to 900
    /Henrik.

  2. #2
    cbrun17's Avatar
    cbrun17 Guest


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    Hi Henrik,

    No worries... I learn from mistakes as well. I appreciate all your help.

    I believe we're getting very close. I have the following code:


    if enc_counter <> enc_counter_old then 'see if value has changed
    enc_counter_old = enc_counter 'move new value to old
    Counter = enc_counter
    Sign = Counter.15 ' Save sign
    Counter = ((ABS Counter) * 5) ' Degrees is now 0 to 900

    If Sign THEN Counter = -Counter ' Restore sign, value is now -900 to 900

    lcdout $FE,1, sdec Counter / 10, ".", sdec Counter //10
    lcdout $FE,$C0, sdec Counter


    First, the counter increments by 5.
    Also, in the first LCD statement, the count increments to +90 as expected. However, the negative value shows as 6553.1, 6552.6, etc...
    The second LCD out statement displays +/- 900 as you explained.

    Seems the absolute value gets lost when trying to display negative after formatting.

    Is there maybe a way I can parse the decimal values prior to display statement to maintain the value?

    Chris
    Last edited by cbrun17; - 7th February 2017 at 16:15.

  3. #3
    cbrun17's Avatar
    cbrun17 Guest


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    I made some changes to the formatting and I successfully display the +/- values. Displays +/- xx.x. I updated the code to:

    if enc_counter <> enc_counter_old then 'see if value has changed
    enc_counter_old = enc_counter 'move new value to old
    Counter = enc_counter
    Sign = Counter.15 ' Save sign
    Counter = ((ABS Counter) * 5) ' Degrees is now 0 to 900
    If Sign THEN Counter = -Counter ' Restore sign, value is now -900 to 900

    If NOT sign then
    lcdout $FE,1, sdec Counter /10 , ".", sdec Counter // 10
    else
    lcdout $FE,1, "-", sdec -Counter /10 , ".", sdec -Counter // 10
    endif

    This takes care of displaying properly. Now is there a way I can bring the count down to .25 increments instead of .5?

    Thanks again.

    Chris

  4. #4
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,621


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    Hi,
    Great job! The problem with the display routine is, again, that PBP doesn't handle math with negative numbers properly so the result of the Counter / 10 operation gets messed up when Counter is negative.

    You can probably make that snippet a bit more efficient, somelike like this perhaps:
    Code:
    if enc_counter <> enc_counter_old then 'see if value has changed
    enc_counter_old = enc_counter 'move new value to old
    Counter = enc_counter 
    
    Sign = Counter.15 ' Save sign
    Counter = ((ABS Counter) * 5) ' Degrees is now 0 to 900
    
    LCDOUT $FE, 1  ' Clear screen
    IF Sign THEN
      LCDOUT "-"    ' Print a minus sign if the value is negative
    ENDIF
    LCDOUT SDEC Counter / 10, ".", SDEC Counter // 10   ' now print the value
    I must admit that I can't really follow the ASM code for the interrupt but since it's using Interrupt on change I think it "should" do x4 decoding so you "should" get 1440 counts per revoultion on a 360CPR encoder. I don't know why that's not happening. Hopefully someone else is able to help with that part.

    /Henrik.

  5. #5
    cbrun17's Avatar
    cbrun17 Guest


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    Thank you Henrik.

    I couldn't have made it this far without you're help. The displayed number is perfect. As you said, the counts aren't making much sense. I'll continue to experiment with this to see if I can figure out how to get the correct resolution.

    Thanks again and kind regards....

    Chris

  6. #6
    Join Date
    May 2013
    Location
    australia
    Posts
    2,680


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    you have some isr issues
    1.
    ; Not Necessary for Chips with >2k of Codespace
    ; movwf wsave
    ; swapf STATUS, W
    ; clrf STATUS
    ; movwf ssave
    ; movf PCLATH, W
    ; movwf psave
    ; Not Necessary for Chips with >2k of Codespace what ?
    yet at the end of your isr pclath and status are restored
    all isrs need to save and restore mcu status

    2.
    the chip you are using has more than 1 code pages ,your isr is located in page 0 ,
    pclath should be set accordingly
    USUAL
    ISR BEGIN
    movwf wsave
    swapf STATUS, W
    clrf STATUS
    movwf ssave
    movf PCLATH, W
    movwf psave
    CLRF PCLATH
    YOUR CODE
    .............
    3.
    a RE stream moving clockwise goes phase 1 - 2 - 3 - 4 -1 -2 .......
    where phase 1=00
    2=10
    3=11
    4=01
    your code
    ;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


    say enc_old=01 ,enc_new=00 ; ie clockwise move
    your xor would yield 0b01000000
    and bit 7 is clear so no move is recorded , wrong it did move .it may also cause the 0 point to creep as direction
    changes occur
    the test should be is the result 0 not is bit7 0

    4. bank sel ?
    your isr forces bank0 the chip has more than one bank available
    all vars used in isr need to be assigned to bank 0
    ie enc_old VAR BYTE bank0 .
    etc
    5. enc_counter a word a non atomic entity
    any non atomic entity that can be changed in an isr and is used outside the isr needs to be handled
    correctly to prevent glitches caused by rollover/under of the low byte.
    best way is to use a buffered copy or that entity
    eg

    enc_counter VAR WORD bank0
    buffered_enc_counter VAR WORD
    gosub get_enc_count


    get_enc_count:
    intcon.3=0 ; rbc_int off
    buffered_enc_counter=enc_counter
    intcon.3=1 ; rbc_int on
    return
    Last edited by richard; - 8th February 2017 at 02:44.
    Warning I'm not a teacher

  7. #7
    cbrun17's Avatar
    cbrun17 Guest


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    Hi Richard,

    Thank you very much for the information. It's a little over my head at this point with regard to asm. I'm still very new to asm coding, but I'm going to try working through your suggestions and see what I can accomplish.

    Chris
    Last edited by cbrun17; - 8th February 2017 at 22:34.

  8. #8
    cbrun17's Avatar
    cbrun17 Guest


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    Hi Richard,

    Here is what I've done so far. Since I'm just learning asm (and coding in general), I made the changes you suggested as I understand them (haven't tried to compile yet). The part I am still not too sure about is the reference to is testing bit7. Are you saying I should not be testing bit7 (btfsc _enc_old,7)? How do I reference this in the asm code?

    I've tried going through this step by step and it's starting to make a little sense. I guess until I've done this a few times, I'll have to ask some lame questions....

    Again, I really appreciate your input.

    Code:
    'Read Quadrature Encoder and display value on LCD. for ****pic16f876****
    
    '************************ 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 bank0
    enc_counter_old  	  VAR    WORD bank0
    enc_scaler 	  	  VAR    WORD bank0
    
    
    '*********************** 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
    	
    	; ****Changed from original isr initialization settings****
            ;movwf   wsave
            ;swapf   STATUS, W
            ;clrf    STATUS
            ;movwf   ssave
            ;movf    PCLATH, W
            ;movwf   psave  
             
    ; Changed from original isr initialization settings
    	movwf wsave
     	swapf STATUS, W
     	clrf STATUS
     	movwf ssave
     	movf PCLATH, W
     	movwf psave 
     	CLRF PCLATH
    
    
    	;====== 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.       ************Not sure what needs to happen here**************
            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 ************Not sure what needs to happen here**************
          	                         ;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 1000		    ' just wait a bit to read splash screen
    enc_counter = 0	' set default encoder value
    enc_counter_old = 0	
    
            ' Initial display
        LCDOUT $FE,1,"ENCODER"  ' Initial display
        LCDOUT $FE,$C0, "00.00"
    
    INTCON = %10001000      ' Enable PortB Change Interupts  
    
    
    '************************** TESTING ROUTINES HERE **************************
    
    test:
    	  
                if buffered_enc_counter <> enc_counter_old then 'see if value has changed
                   	'enc_counter_old = enc_counter 'move new value to old ******Removed this line and replaced with*******
                     
                    gosub get_enc_count                                  '******this line******************************
    
     		 Counter = buffered_enc_counter
     
     		Sign = Counter.15 ' Save sign
     		Counter = ((ABS Counter) * 5) ' Degrees is now 0 to 900
     	If Sign THEN Counter = -Counter ' Restore sign, value is now -900 to 900
    
     	   If NOT sign then
     		LCDOUT $FE,1, sdec Counter /10 , ".", sdec Counter // 10 
     	   else
     		LCDOUT $FE,1, "-", sdec -Counter /10 , ".", sdec -Counter // 10
     	   endif
    
    '*********************************************************************************
    
                    low led		                        'turn off CCW led
                    low led2	                        'turn off CW led
                endif
    goto test
    
    get_enc_count:
     intcon.3=0 ; rbc_int off
     buffered_enc_counter=enc_counter
     intcon.3=1 ; rbc_int on
     return
    Last edited by cbrun17; - 9th February 2017 at 18:08.

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 : 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