decoding quadrature encoders


Closed Thread
Results 1 to 40 of 94

Hybrid View

  1. #1
    Join Date
    Oct 2003
    Location
    holland
    Posts
    251


    Did you find this post helpful? Yes | No

    Default

    Here is a small example how to count up and down a position counter.
    How to initialise the position counter you must do by yourself.

    A_INPUT VAR PORTA.0
    B_INPUT VAR PORTA.1

    HULP1 VAR BIT
    HULP2 VAR BIT

    COUNTER VAR WORD

    Clear
    COUNTER = 32768

    START:


    HULP2 = A_INPUT & ~ HULP1 'EVERY POSITIVE GOWING EDGE OF A_INPUT
    HULP1 = A_INPUT 'GIVES A PULSE OF ONE PROGRAMM CYCLE

    IF HULP2 = 1 AND B_INPUT = 1 THEN 'MOTOR TURNS RIGHT
    COUNTER = COUNTER + 1
    ENDIF

    IF HULP2 = 1 AND B_INPUT = 0 THEN 'MOTOR TURNS LEFT
    COUNTER = COUNTER - 1
    ENDIF

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


    Did you find this post helpful? Yes | No

    Default

    Hi!

    Maybe these two links will help you out.

    Nuts and Volts:
    http://www.parallax.com/dl/docs/cols...l1/col/nv8.pdf

    See State machine: two-bit quadrature encoder
    http://www.emesystems.com/BS2fsm.htm#twobit%20encoder

    Luciano

  3. #3
    ice's Avatar
    ice Guest


    Did you find this post helpful? Yes | No

    Default

    mat janssen>>
    Thank yoiu very much for the code snip..but i cant understand whats going on in the code ,esp the "&~" part
    I tried using the code...however ,whatever maybe the direction the motor is running ....i get the same reading ,say always "left"



    Luciano>>
    Thanks for the links..
    i ported the foll code from nv8 [http://www.parallax.com/dl/docs/col...ol1/col/nv8.pdf --pg7]
    Code:
        let new = pins & %11000000 ' Mask off all but bits 6 & 7
    start:
       let old = new & %11000000 ' Mask bits and copy new into old.
    again:
       let new = pins & %11000000 ' Copy encoder bits to new.
       if new = old then again ' If no change, try again.
       let directn = bit6 ^ bit15 ' XOR right bit of new w/ left bit of old.
       if directn = 1 then CW ' If result=1, encoder turned clockwise.
       serout 0,n2400,(I,left) ' If result=0, counterclock (scroll left).
       goto start ' Do it again.
    CW:
       serout 0,n2400,(I,right) ' Clockwise (scroll right).
       goto start ' Do it again.

    To pbp


    Code:
    old VAR BYTE
    new VAR BYTE
    direct VAR BIT
    
    quad:
    	new= PORTC && %00000011
    	
    start:
    	old=new && %00000011
    
    again:
    	new= PORTC && %00000011
    	Pause 10
    	LCDOut $fe,1,"no movement"	
    	IF new=old Then again
    	direct=new.bit1 ^ old.bit0	
    	IF direct=0 Then GoTo cw
    	LCDOut $fe,1,"one dir"
    	Pause 10
    	GoTo start
    		
    cw:
    	LCDOut $fe,1,"2nd dir"
    	Pause 10
    	GoTo start
    with the above code...there is alot of fluctuation between the 2 directions..

    ..anything going wrong in my code?.i sat on it the whole day..still cant figure out the problem

  4. #4
    Join Date
    Oct 2003
    Location
    holland
    Posts
    251


    Did you find this post helpful? Yes | No

    Default

    Hallo,
    I completed the programm for testing and it works ok.
    With the & ~ look in the manual it takes the not value of the bit.

    here is the complete source and also the hex file

    'PIC 16F628A test


    @ DEVICE PIC16F628A,INTRC_OSC
    @ DEVICE PIC16F628A,MCLR_OFF
    @ DEVICE PIC16F628A,BOD_OFF
    @ DEVICE PIC16F628A,LVP_OFF
    @ DEVICE PIC16F628A,CPD_OFF
    @ DEVICE PIC16F628A,PROTECT_OFF


    DEFINE OSC 4




    CMCON = 7
    VRCON = 0
    OPTION_REG.7 = 0

    TRISA = %11111111
    TRISB = %00000000


    A_INPUT VAR PORTA.0
    B_INPUT VAR PORTA.1

    HULP1 VAR BIT
    HULP2 VAR BIT

    COUNTER VAR WORD

    Clear

    COUNTER = 128

    START:


    HULP2 = A_INPUT & ~ HULP1 'EVERY POSITIVE GOWING EDGE OF A_INPUT
    HULP1 = A_INPUT 'GIVES A PULSE OF ONE PROGRAMM CYCLE

    IF HULP2 = 1 AND B_INPUT = 1 Then 'MOTOR TURNS RIGHT
    COUNTER = COUNTER + 1
    EndIF

    IF HULP2 = 1 AND B_INPUT = 0 Then 'MOTOR TURNS LEFT
    COUNTER = COUNTER - 1
    EndIF

    PORTB = COUNTER
    GoTo START
    Attached Files Attached Files

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


    Did you find this post helpful? Yes | No

    Default

    Hello ICE!

    You need the Bitwise Operator:
    &

    Not the logical Operator AND:
    && (AND and && are the same)


    From the PBP manual:

    4.17.14. Bitwise Operators

    Bitwise operators act on each bit of a value in boolean fashion.
    They can be used to isolate bits or add bits into a value.


    4.19. Logical Operators

    Logical operators differ from bitwise operations. They yield a true/false
    result from their operation. Values of 0 are treated as false. Any other
    value is treated as true.


    Luciano
    Last edited by Luciano; - 8th April 2005 at 12:33.

  6. #6
    ice's Avatar
    ice Guest


    Did you find this post helpful? Yes | No

    Default

    Luciano,
    I tried it with & [bitwise operators]...actually everything..
    still no go...

    mat janssen>>
    are u by any chance using a dual D f/f between the encoder and the PIC,because im not..
    Thanks for ur code snip
    Last edited by ice; - 8th April 2005 at 13:52.

  7. #7
    Join Date
    Oct 2003
    Location
    holland
    Posts
    251


    Did you find this post helpful? Yes | No

    Default

    No, this small programm reacts as a D flipflop.
    see attached drawing what happens
    Attached Images Attached Images  

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


    Did you find this post helpful? Yes | No

    Default

    Hello ICE,

    From the PDF file page 3:
    http://www.parallax.com/dl/docs/cols...l1/col/nv8.pdf

    In the case of the encoder sequence, it turns out that for
    any given sequence, XORing the righthand bit of the old value
    with the lefthand bit of the new value tells you the direction
    of rotation.
    For example, take the clockwise sequence 01 00: 1 XOR 0 = 1.
    Now the counter-clockwise sequence 01 11: 1 XOR 1 = 0.
    This relationship holds for any pair of numbers in either direction.

    * * *
    The code for the above is:

    direction_bit = old.bit0 ^ new.bit1 ' 1 if CW, 0 if CCW

    * * *
    In your code:

    Pause 10 '= 10 milliseconds

    * * *
    This will help you to debug your code.

    Remove the line
    LCDOut $fe,1,"no movement"

    Replace the lines
    LCDOut $fe,1,"one dir" with LCDOut $fe,1,"Last direction was CCW"
    LCDOut $fe,1,"2nd dir" with LCDOut $fe,1,"Last direction was CW"


    Luciano

  9. #9
    Join Date
    Apr 2011
    Location
    León
    Posts
    5


    Did you find this post helpful? Yes | No

    Lightbulb Re: decoding quadrature encoders

    Quote Originally Posted by mat janssen View Post
    Hallo,
    I completed the programm for testing and it works ok.
    With the & ~ look in the manual it takes the not value of the bit.

    here is the complete source and also the hex file

    'PIC 16F628A test


    @ DEVICE PIC16F628A,INTRC_OSC
    @ DEVICE PIC16F628A,MCLR_OFF
    @ DEVICE PIC16F628A,BOD_OFF
    @ DEVICE PIC16F628A,LVP_OFF
    @ DEVICE PIC16F628A,CPD_OFF
    @ DEVICE PIC16F628A,PROTECT_OFF


    DEFINE OSC 4




    CMCON = 7
    VRCON = 0
    OPTION_REG.7 = 0

    TRISA = %11111111
    TRISB = %00000000


    A_INPUT VAR PORTA.0
    B_INPUT VAR PORTA.1

    HULP1 VAR BIT
    HULP2 VAR BIT

    COUNTER VAR WORD

    Clear

    COUNTER = 128

    START:


    HULP2 = A_INPUT & ~ HULP1 'EVERY POSITIVE GOWING EDGE OF A_INPUT
    HULP1 = A_INPUT 'GIVES A PULSE OF ONE PROGRAMM CYCLE

    IF HULP2 = 1 AND B_INPUT = 1 Then 'MOTOR TURNS RIGHT
    COUNTER = COUNTER + 1
    EndIF

    IF HULP2 = 1 AND B_INPUT = 0 Then 'MOTOR TURNS LEFT
    COUNTER = COUNTER - 1
    EndIF

    PORTB = COUNTER
    GoTo START
    I'm trying to do the example with PIC16f877a but i have problems for example the VRCON = 0 my compiler says it's a bad instruction how can i probe with this pic

    thanks

  10. #10
    Join Date
    Nov 2003
    Location
    Greece
    Posts
    4,170


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    877A does not have VRCON register. Instead it has CVRCON.

    Look in the P16F877A.inc file in the C:\Program Files\Microchip\MPASM Suite path.

    Also pf of the chip you are using is a must.

    Ioannis

  11. #11
    Join Date
    Apr 2011
    Location
    León
    Posts
    5


    Did you find this post helpful? Yes | No

    Question Re: decoding quadrature encoders

    Hi Ioannis exactly i'm using PIC16f877a i have the same problem that ice i cant see my count on my LCD and i cant see using pbp
    only add my count never sub my count do you know what's my problem?

    i tried all in my hands but i continue with same errors
    My code is:

    Code:
    '****************************************************************
    '*  Name    : UNTITLED.BAS                                      *
    '*  Author  : .                                                            *
    '*  Notice  : Copyright (c) 2011 [Ing..]  *
    '*          : All Rights Reserved                               *
    '*  Date    : 19/04/2011                                        *
    '*  Version : 1.0                                               *
    '*  Notes   :                                                   *
    '*          :                                                   *
    '****************************************************************
    'Parte de configuraciÛn de LCD
    DEFINE LCD_DREG PORTB
    DEFINE LCD_DBIT  4
    DEFINE LCD_RSREG PORTB
    DEFINE LCD_RSBIT 1
    DEFINE LCD_EREG  PORTB
    DEFINE LCD_EBIT  3
    DEFINE OSC 4
    
    contador var byte
    ON INTERRUPT GOTO SUMA
    INTCON.4 = 1
    INTCON.7 = 1
    OPTION_REG.6 = 1  
    CMCON=7
    CVRCON=0
    
    TRISB = %00000101
    CONTADOR = 12
    
    principal:
    lcdout $FE,1,"ENCODER= ",#CONTADOR," VALOR "
    pause 500
    goto principal
    DISABLE
    end
    
    SUMA:
    IF PORTB.0 = 1 THEN
        OPTION_REG.6 = 0
        IF PORTB.2 = 1 THEN
        contador = contador + 1
        ENDIF
    else
        OPTION_REG.6 = 1
        IF PORTB.2 = 1 THEN
        CONTADOR = CONTADOR - 1
        ENDIF
    ENDIF
    INTCON.1 = 0
    RESUME
    ENABLE

  12. #12
    Join Date
    Oct 2005
    Posts
    34


    Did you find this post helpful? Yes | No

    Default Re: decoding quadrature encoders

    Hi mat I see you are using ASM to set up your config word, it is a greate idea, but I get a compiler error when I do it. I always edit the device include file 16F628.INC:
    NOLIST
    ifdef PM_USED
    LIST
    include 'M16F62x.INC' ; PM header
    device pic16F628, intrc_osc, wdt_off, pwrt_off, mclr_off, lvp_off, protect_off
    XALL
    NOLIST

    You can't teach an old dog new tricks, but I'm always willing to try.

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

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

Similar Threads

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

Members who have read this thread : 1

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