HEDS5540 optical encoder


Closed Thread
Results 1 to 24 of 24

Hybrid View

  1. #1

    Default HEDS5540 optical encoder

    Hello boys,
    I'm new on the forum and beginner in programming. I try to create a simple digital readout for X-axis on to my small lathe.
    For simplicity I want to use rotary encoder and rack and pinion drive to measure distance.
    I read and try all suitable codes I have found on the forum but a little success. I try combine this pieces of codes, but no success.
    Please can someone to help me how to implement this code to functional end. MCU is 16F628A thank You for help.
    This is my code:
    Code:
    INCLUDE "DT_INTS-14.bas"        ; Base Interrupt System
    INCLUDE "ReEnterPBP.bas"        ; Include if using PBP interrupts
     
    
    '                       PIC 16F628A
    ' PicBasic program to demonstrate operation of an LCD in 4-bit mode
    '          and reading an optical encoder in quadrature
    '
    ' LCD should be connected as follows:
    '       LCD     PIC
    '       DB4     PortA.0
    '       DB5     PortA.1
    '       DB6     PortA.2
    '       DB7     PortA.3
    '       RS      PortA.4 (add 4.7K pullup resistor to 5 volts)
    '       E       PortB.0
    '       RW      Ground
    '       Vdd     5 volts
    '       Vss     Ground
    '       Vo      20K potentiometer (or ground)
    '       DB0-3   No connect
    ;       encoder ch A PortB.6
    ;       encoder ch B PortB.7
    
    #config
       __config _HS_OSC & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _LVP_OFF & _CPD_OFF 
      #endconfig
      
    DEFINE OSC 20  
      
    TRISA    = %00000000 ' Make all PortA pins output
    TRISB    = %11100000
    
    Define LCD_DREG PORTA
    Define LCD_DBIT 0
    Define LCD_RSREG PORTA
    define LCD_RSBIT 4
    define LCD_EREG PORTB
    define LCD_EBIT 0
    define LCD_BITS 4
    define LCD_LINES 2
    define LCD_COMMANDUS 2000
    define LCD_DATAUS 50
    
    Flag var bit
    wsave VAR BYTE    $70     SYSTEM  ; alternate save location for W 
    enc_new VAR BYTE
    enc_old VAR BYTE
    enc_counter VAR WORD
    
    ; Set variable value @ startup
    Flag = 0          
    enc_new = 0
    enc_old= 0
    enc_counter = 0
            
    
    Lcdout $fe, 1                       ' Clear LCD screen
    Lcdout "Encoder test"               ' Display message
    Pause 500 
    @ INT_ENABLE RBC_INT            ; enable external (INT) interrupts
    goto main_loop
    
    
    asm
    INT_LIST  macro    ; IntSource,        Label,  Type, Resetflag?
            INT_Handler    RBC_INT, _enc,  ASM,  yes
        endm
        INT_CREATE                      ; Creates the interrupt processor
        ;================================================
    endasm
    enc:
    asm    
    
        ;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 =====
        endasm                     
    @ INT_RETURN
    
    Main_Loop:   
      
    if Flag = 1 then
    Lcdout $fe, 1                    
    Lcdout Dec enc_counter     ; display counter value on LCD
    Flag = 0
    endif
    goto Main_Loop
    end
    Last edited by louislouis; - 2nd October 2016 at 20:59.

  2. #2
    Join Date
    May 2013
    Location
    australia
    Posts
    2,632


    Did you find this post helpful? Yes | No

    Default Re: HEDS5540 optical encoder

    if Flag = 1 then
    nowhere in your code is flag ever set to 1
    Warning I'm not a teacher

  3. #3


    Did you find this post helpful? Yes | No

    Default Re: HEDS5540 optical encoder

    you're right, in the ASM code I not have defined the flag=1. The question is how to do that.

  4. #4
    Join Date
    May 2013
    Location
    australia
    Posts
    2,632


    Did you find this post helpful? Yes | No

    Default Re: HEDS5540 optical encoder

    easiest way


    Code:
    Flag var byte
    wsave VAR BYTE    $70     SYSTEM  ; alternate save location for W 
    enc_new VAR BYTE
    enc_old VAR BYTE
    enc_counter VAR WORD


    Code:
    asm    
    
        ;Read latest input from PORTB & put the value in _enc_new.
             MOVE?CB    1 ,_Flag
             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
    Last edited by richard; - 2nd October 2016 at 22:58. Reason: CASE SENSITIVE
    Warning I'm not a teacher

  5. #5


    Did you find this post helpful? Yes | No

    Default Re: HEDS5540 optical encoder

    Richard's way is the best. Just for referece it could also be done without asm since its not time sensitive.

    Code:
    old_enc_counter var word
    Main_Loop:   
      
    if enc_counter<> old_enc_counter then 'If change update LCD
    Lcdout $fe, 1                    
    Lcdout Dec enc_counter     ; display counter value on LCD
    old_enc_counter = enc_counter
    endif
    goto Main_Loop
    end

  6. #6


    Did you find this post helpful? Yes | No

    Default Re: HEDS5540 optical encoder

    hmm, I try it and no change, still only the "encoder test" message on the LCD.
    This line is ok MOVE?CB 1 ,_Flag what means the question mark.

  7. #7
    Join Date
    May 2013
    Location
    australia
    Posts
    2,632


    Did you find this post helpful? Yes | No

    Default Re: HEDS5540 optical encoder

    This line is ok MOVE?CB 1 ,_Flag what means the question mark.
    NOTHING , MOVE?CB IS THE NAME OF A PBP MACRO that moves a constant to a byte var
    advantage is that the macro does all the banksel for you

    more proper would be FOR YOUR isr vars

    Code:
    Flag var bit  bank0
    enc_new VAR BYTE  bank0
    enc_old VAR BYTE  bank0
    enc_counter VAR WORD  bank0
    Warning I'm not a teacher

  8. #8
    Join Date
    May 2013
    Location
    australia
    Posts
    2,632


    Did you find this post helpful? Yes | No

    Default Re: HEDS5540 optical encoder

    plus I would rearrange the code like this , the way you have it the interrupt is enabled before its created
    which may not be a good thing


    Code:
     INCLUDE "DT_INTS-14.bas"        ; Base Interrupt System
    INCLUDE "ReEnterPBP.bas"        ; Include if using PBP interrupts
     
    '                       PIC 16F628A
    ' PicBasic program to demonstrate operation of an LCD in 4-bit mode
    '          and reading an optical encoder in quadrature
    '
    ' LCD should be connected as follows:
    '       LCD     PIC
    '       DB4     PortA.0
    '       DB5     PortA.1
    '       DB6     PortA.2
    '       DB7     PortA.3
    '       RS      PortA.4 (add 4.7K pullup resistor to 5 volts)
    '       E       PortB.0
    '       RW      Ground
    '       Vdd     5 volts
    '       Vss     Ground
    '       Vo      20K potentiometer (or ground)
    '       DB0-3   No connect
    ;       encoder ch A PortB.6
    ;       encoder ch B PortB.7
     
    Define LCD_DREG PORTA
    Define LCD_DBIT 0
    Define LCD_RSREG PORTA
    define LCD_RSBIT 4
    define LCD_EREG PORTB
    define LCD_EBIT 0
    define LCD_BITS 4
    define LCD_LINES 2
    define LCD_COMMANDUS 2000
    define LCD_DATAUS 50
    
    wsave VAR BYTE    $70     SYSTEM  ; alternate save location for W 
    enc_new VAR BYTE   bank0
    enc_old VAR BYTE   bank0
    enc_counter VAR WORD   bank0
    Flag var BYTE        bank0
            
    Lcdout $fe, 1                       ' Clear LCD screen
    Lcdout "Encoder test"               ' Display message
    Pause 500 
     
    
    asm
    INT_LIST  macro    ; IntSource,        Label,  Type, Resetflag?
            INT_Handler    RBC_INT, _enc,  ASM,  yes
        endm
        INT_CREATE                      ; Creates the interrupt processor
        ;================================================
    endasm
    ; Set variable value @ startup
    Flag = 0          
    enc_new = 0
    enc_old= 0
    enc_counter = 0
    @ INT_ENABLE RBC_INT            ; enable external (INT) interrupts
    Main_Loop:     
    if Flag = 1 then
    Lcdout $fe, 1                    
    Lcdout Dec enc_counter     ; display counter value on LCD
    Flag = 0
    endif
    goto Main_Loop
    end
     
     
     enc:
    asm    
        ;Read latest input from PORTB & put the value in _enc_new.
             MOVE?CB 1,_Flag
             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
             INT_RETURN
        ;============ END OF THE ROTARY ENCODER CODE =====
        endasm
    Warning I'm not a teacher

  9. #9
    Join Date
    May 2013
    Location
    australia
    Posts
    2,632


    Did you find this post helpful? Yes | No

    Default Re: HEDS5540 optical encoder

    tried your code on a 500 ppr opto encoder, it seems to be good

    modified to run on 16f1825, full quad decode [2000 edges / rev]

    Code:
      #CONFIG
                 __config        _CONFIG1,    _FOSC_INTOSC & _CP_OFF & _WDTE_ON  &  _PWRTE_ON  &  _MCLRE_ON  & _CLKOUTEN_OFF
                  __config      _CONFIG2, _PLLEN_ON & _LVP_OFF            
    #ENDCONFIG
     
    OSCCON=$70 
    DEFINE OSC 32
    
    INCLUDE "DT_INTS-14.bas"        ; Base Interrupt System
    INCLUDE "ReEnterPBP.bas"        ; Include if using PBP interrupts
     
    '                       PIC 16F1825
    
    ;       encoder ch A Porta.4
    ;       encoder ch B Porta.5
    
    enc_new VAR BYTE   bank0
    enc_old VAR BYTE   bank0
    enc_counter VAR WORD   bank0
    Flag var BYTE        bank0
     
    TRISA     = %111111    ' Make all pins Input 
    trisc     = %11111111   ;Make all pins Input   
    ANSELA=0     
    ANSELC=0
    ;SETUP IOC REGS
    IOCAP  =    %110000     ;POS EDGE
    IOCAN  =    %110000      ;NEG EDGE
    IOCAF  =     0             ;CLR INT FLAG
    intcon.3 = 1     ;iocie
    asm
    INT_LIST  macro    ; IntSource,        Label,  Type, Resetflag?
            INT_Handler    IOC_INT, _enc,  ASM,  NO
        endm
        INT_CREATE                      ; Creates the interrupt processor
        
    endasm
      ;debug   --------------------------
     TRISA.0     = 0
     lata.0=1
     pause 2000     ;debug
     serout2 PORTa.0,84, ["ready v3",13,10 ]    ;debug
      ;debug ------------------------------------
     
    ; Set variable value @ startup
    Flag = 0          
    enc_new = 0
    enc_old= 0
    enc_counter = 0
    @ INT_ENABLE IOC_INT
    Main_Loop:     
    if Flag = 1 then
     serout2 PORTa.0,84, [#enc_counter,13,10 ]  
    Flag = 0
    endif
    goto Main_Loop
    end
     
     
      enc:
    asm 
        ;Read latest input from PORTA & put the value in _enc_new.
             MOVE?CB 1,_Flag 
             CHK?RP   PORTA
             movf    PORTA,W
             RST?RP
             movwf  _enc_new
             ;Strip off all but the 2 MSBs in _enc_new.
             movlw    B'00110000'    ;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,5         ;Test bit 5 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
             CHK?RP   IOCAF
             CLRF IOCAF
             INT_RETURN
        ;============ END OF THE ROTARY ENCODER CODE =====
        endasm
    Warning I'm not a teacher

  10. #10


    Did you find this post helpful? Yes | No

    Default Re: HEDS5540 optical encoder

    Thank You Richard for helping me. The your corrected code works, the routine counts up and down when I turn the encoder shaft cw or ccw, but if i mark the shaft starting position and turn on the MCU the reading is zero thats OK and when i slowly turn for example half turn and go back to marked position the encoder reading is not zero but less or more than zero. I think the routine not count correctly the encoder pulses, maybe the encoder send incorrect signals, I dont know.

  11. #11
    Join Date
    May 2013
    Location
    australia
    Posts
    2,632


    Did you find this post helpful? Yes | No

    Default Re: HEDS5540 optical encoder

    Thank You Richard for helping me. The your corrected code works, the routine counts up and down when I turn the encoder shaft cw or ccw, but if i mark the shaft starting position and turn on the MCU the reading is zero thats OK and when i slowly turn for example half turn and go back to marked position the encoder reading is not zero but less or more than zero. I think the routine not count correctly the encoder pulses, maybe the encoder send incorrect signals, I dont know
    .

    I tried my setup again more carefully and verified it works ok, several back and forth motions always returns to the zero mark as zero in either direction, so the question is does rbc_int on your chip react to every edge generated by the encoder the same as ioc_int on a 16f1825 / perhaps not
    Warning I'm not a teacher

  12. #12


    Did you find this post helpful? Yes | No

    Default Re: HEDS5540 optical encoder

    Yes, that's the problem. The RBC_INT react with pin RB.0 on port B, because this pin operate the LCD ENABLE pin. If I try to use only portA to operate the LCD (with internal 4MHz oscillator) and on the PORTB pin RB.6 and RB.7 hang only the encoder A and B channel, rest pins configured to output the reading is perfect. Always after turning up and down randomly, slower, quicker etc. when I return to marked starting position the reading is zero. Now, how to solve this problem, how to "tell" to MCU the only pinRB.6 and RB.7 trigger the interrupt, not the entire portB

  13. #13
    Join Date
    May 2013
    Location
    australia
    Posts
    2,632


    Did you find this post helpful? Yes | No

    Default Re: HEDS5540 optical encoder

    "
    Code:
    If I try to use only portA to operate the LCD (with internal 4MHz oscillator) and on the PORTB pin RB.6 and RB.7 hang only the encoder A and B channel, rest pins configured to output the reading is perfect.

    yes , makes sense . unfortunately setting or clearing a bit on a pic port entails a read of that port first , any read of portb clears the mismatch condition state for the rbc interrupt.
    so even though only the bits 4 to 7 that are set as inputs can cause an interrupt on change a write to your "e" pin can cause changes to be missed .
    if it won't keep up with the encoder using the int osc then use an i2c lcd display or a different pic
    Warning I'm not a teacher

Similar Threads

  1. 2 Beam Optical Pulse Generator
    By WOZZY-2010 in forum Schematics
    Replies: 8
    Last Post: - 6th April 2010, 04:03
  2. USB Optical mouse as motor encoder
    By RodSTAR in forum mel PIC BASIC Pro
    Replies: 6
    Last Post: - 24th August 2008, 15:09
  3. optical voice link
    By Macgman2000 in forum mel PIC BASIC Pro
    Replies: 6
    Last Post: - 18th May 2008, 21:11
  4. Using a optical coder
    By debutpic in forum mel PIC BASIC Pro
    Replies: 22
    Last Post: - 16th October 2007, 04:57
  5. Using the optical sensor from a ball mouse
    By lsteele in forum General
    Replies: 5
    Last Post: - 5th September 2007, 16:45

Members who have read this thread : 0

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