decoding quadrature encoders


Closed Thread
Results 1 to 40 of 94

Hybrid View

  1. #1
    Join Date
    Sep 2004
    Location
    montreal, canada
    Posts
    6,898


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    With PBP3 you no longer need to comment out the INC file.

    BTW you should'nt edit it each and every time. Comment out the DEVICE and CONFIG lines in the INC file, save it... and set the fuses on your code period.

    If you still experiment compiler error or warning, check out the syntax used.. is it PM or MPASM... PM need DEVICE line(s), while MPASM need __CONFIG line(s)

    Better get familiar with MPASM syntax though... PBP3 use it, PM is no longer supported.
    Steve

    It's not a bug, it's a random feature.
    There's no problem, only learning opportunities.

  2. #2
    cbrun17's Avatar
    cbrun17 Guest


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    Hello,

    I know this is an old thread but I have reached the extent of my knowledge with PBP. I have been testing the code published by several forum members and the code works wonderful. Using the asm interrupts provides very fast response and no missed counts. The issue I have is trying to display the results on LCD in 4 digit result (xx.xx). I'm using the following hardware:

    16F876 @ 20MHz
    US digital S4T-360 (360 CPR)
    2x16 LCD connected to PortA

    I'm using the encoder to measure angles from a center position (12 o'clock). It can turn +/- 90 degrees. I need to display the results as +/- any value between 0 and 90 such as 46.15 or -58.23 (cw,ccw respectively).

    Since the count value is one byte, I can't figure out a way to get greater resolution for the count. I'm very new to programming period!!! I realize I have a lot to learn and I will continue to learn.
    I've tried using sdec enc_Count/10, enc_count//10 in the LCD lines but the results are not giving me what I need.

    Maybe I'm missing something really simple and I hope someone can point me in the right direction.

    Here is the code I'm using:


    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 1000		    ' just wait a bit to read splash screen
    enc_counter = 0	' set default encoder value
    enc_counter_old = 0	
    
    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
               
                 ' Here is were I struggle.....
                 '*********************************************************************************
                    lcdout $fe,$C0, SDEC2 enc_counter/10, ".", SDEC2 enc_counter//10     	'display enc_counter value
                 '*********************************************************************************
    
                    low led		                        'turn off CCW led
                    low led2	                        'turn off CW led
                endif
    goto test
    Thank you in advance,

    cbrun17

  3. #3
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    Hi,
    If I'd venture a guess it would be that with a 360CPR encoder and 4x decoding the counter variable will "tick" 4 counts per degree but your math assumes the resolution is 0.1 degrees.

    As a simple workaround try multiplying your count value by 2.5 before you enter your display routine.
    Code:
    ' Here is were I struggle.....
    enc_counter = enc_counter */ 640   ' Multiply by 2.5
    '*********************************************************************************
    lcdout $fe,$C0, SDEC2 enc_counter/10, ".", SDEC2 enc_counter//10     	'display enc_counter value
    '*********************************************************************************
    Obviously, the encoder only provides 0.25 degree resolution and no math can change that.

    If it doesn't work properly then strip out the formating and display the raw value (pre or post multiplying with 2.5) and tell us between what values it ranges.

    /Henrik.

  4. #4
    cbrun17's Avatar
    cbrun17 Guest


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    Hi Henrik,

    Thank you so much for the quick response. I will give your suggestion a try this evening and report back with the results.

    Kind regards,

    Chris

  5. #5
    cbrun17's Avatar
    cbrun17 Guest


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    I changed the multiplier as suggested but I received very erroneous results. Using just "lcdout $fe,$C0, SDEC enc_counter"

    The count results in 2,5,7,10,12,15,,,455. So 2 and 3 counts alternately.
    Then if I multiply enc_counter by 250, I get exactly + 180 degrees which I think confirms 2 "ticks" Also note that by altering the value of enc_counter, I loose my negative count.

    Counting enc_counter without multiplier results in ~ +/- 184.

    To clarify, the shaft has a 12:00 o'clock "zero" position. Turning in either direction from center results in ~ +/- 184. Counting the number of pulses on my Fluke counter indicates the same count.

    So, the way I understand this quadrature coding, it compares the A/B channels to determine the direction. So changing the post result would only be wiped away by any subsequent updates from the interrupt routine.

    I would be satisfied with .25 degree increments if I could get the LCD output correctly. Showing +/- 90 in .25 increments in the form of xx.xx.

    Thank you again for sharing your knowledge.

    Chris

  6. #6
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612


    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.

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

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

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