Rotary encoder with DT interrupts


Closed Thread
Results 1 to 34 of 34

Hybrid View

  1. #1

    Default Rotary encoder with DT interrupts

    Hi All,
    I'm struggling around rotary encoder with push button. I read the forum but not find any successfully solved example.
    Here is my code:
    Code:
    ; PIC16F1847                                 
    
    #CONFIG ; 16FF1847.
            __config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_ON & _CP_OFF & _BOREN_OFF
            __config _CONFIG2, _PLLEN_OFF & _LVP_OFF
    #ENDCONFIG
    
    DEFINE OSC 32
    
    OSCCON = %11110000 ;32 MHz
     
    ANSELA = 0
    ANSELB = 0
    TRISA=%00000000
    TRISB=%00000011
    LATB = %00000011
    
    Include "modedefs.bas"
    INCLUDE "DT_INTS-14.bas" ; Base Interrupt System
    INCLUDE "ReEnterPBP.bas" ; Include if using PBP interrupts 
    
    INTCON=%10001000
    IOCBP=%00000000         
    IOCBN=%00000011         ;on change interrupt  NEGATIVE port RB.0, RB.1
    IOCBF=%00000011  
           
    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
    
    asm
    INT_LIST  macro     ;IntSource,Label, Type, Resetflag?
            INT_Handler    IOC_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 IOC_INT            ; enable external (INT) interrupts
    
    Main:
    if flag=1 then
    SEROUT portb.4,t9600,[#enc_counter,13]
    endif
     
    goto main
    
    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'00000011'    ;Create bit mask (bits 1 & 0).
             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,0         ;Test bit 0 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   IOCBF
             CLRF IOCBF
             INT_RETURN
        ;============ END OF THE ROTARY ENCODER CODE =====
    endasm
    The problem is, I can't get correct encoder reading. In serial monitor I can see the result but the values goes only to increment no matter if I rotate CW or CCW.
    The values increment randomly. I dont know, what is the problem.
    The encoder is wired with 10k pullups and debounce capacitors.
    I need quic response to rotation fot that I try to use this ASM and DT interrupts metod.

    Anyway If I tried another example with no ASM and Interrupts only for testing, the encoder works fine CW and CCW, increments, decrements by one just fine.

    Can someone check my code and correct me what Im doing wrong.
    Thanks.

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


    Did you find this post helpful? Yes | No

    Default Re: Rotary encoder with DT interrupts

    for mechanical encoder with button i use something like this
    Attached Files Attached Files
    Warning I'm not a teacher

  3. #3


    Did you find this post helpful? Yes | No

    Default Re: Rotary encoder with DT interrupts

    Cool, thanks. Both pbp,asm version works. Anyway, I tried two encoder types which I had on hand. Both works, but one count two counts to one detent, and the second count four to one detent.
    I tried set the IOCBP and IOCBN to trigger only on positive edge or only on negative edge or different comination but it doesn't hepl.
    Any trick to adjust this to one detent one count?

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


    Did you find this post helpful? Yes | No

    Default Re: Rotary encoder with DT interrupts

    oneway to count detents divide the absolute raw cnt value by the number of edges/detent ,then restore the sign
    eg
    sb= cnt.15
    detents = abs(cnt)<<2; four edges
    if sb then detents= ~detents+1

    another is to just poll the rotation direction at an acceptable rate
    if clockwise add a count if anticlockwise subtract


    poll: ; call this sub say 5 times / sec or whatever
    if cnt>lastcnt then
    detent=detent+1
    elseif cnt<lastcnt
    detent=detent-1
    endif
    lastcnt=cnt
    return

    another way is to only inc/dec detent counter only when enca and encb both = 1 (ie at detent pos)
    in the re isr
    Warning I'm not a teacher

  5. #5


    Did you find this post helpful? Yes | No

    Default Re: Rotary encoder with DT interrupts

    Hi Richard, sorry for later response. Thanks for pointing me in to right way how to do it. Your first method not works for me, the second method polling works just fine but slows down the response time. The third method inc/dec detent counter only when enca and encb both = 1 works but count every second detent and also slowes down the reading.
    My encoder has 30.position and give 15.pulses/rew. My solution is simply divide the CNT by 2.
    Code:
    enc_isr:
    ev=ev<<2                ;shift last reading into position
    ev=ev|((portb&12)>>2)  ;read enc pins 2,3  mask off others then  combine this reading with last reading
    LOOKUP (ev&$f),[0,255, 1, 0, 1, 0, 0, 255, 255, 0, 0, 1, 0, 1, 255, 0],tmp ;lookup the decision matrix
    if tmp then             ; decide to inc , dec or ignore 
    	if tmp.1 then
    	  cnt=cnt+1
    	else
     	  cnt=cnt-1
    	endif
    endif
    detent=cnt
    detent=detent/2
    By the way, here is the nearly finished product where i used this solution and skills learned here.



    It's a stick welder controller with "advanced" functions like softstart, fan control, pulse welding, antistick, arcforce, hotstart, voltage and current measurement and so on.
    It's based on PIC16F1847, when I used sw multiplexing, multiple interrupts, direct register controls, mixed code ASM/PB and so on.


    Thanks for this great forum and helpful members, happy New Year to all.

  6. #6
    Join Date
    Jun 2005
    Location
    West Australia
    Posts
    116


    Did you find this post helpful? Yes | No

    Default Re: Rotary encoder with DT interrupts

    Hi Folks,

    I'm trying to get my head around what is happening with my attempt at using a Rotary Encoder... it is functional however something weird is happening periodically returning from the interrupt.

    My "I'm Alive" serial string is returned to and finds itself sprinkled among the debug terminal data. I'm sure it is staring at me but I can't see why. Any help appreciated.

    Thanks,
    Bill

    PS
    @ richard
    I persisted for hours trying your method (with appropriate pin changes) and had no joy so went with the Old vs New method. It would only count in one direction with either CW or CCW.

    Code:
    
    '****************************************************************************************************
    '*  Name    : RE test WJS.pbp                                                                       *                                      
    '*  Author  : WJ Sherwood                                                                           *
    '*  Notice  : Copyright (c) 2021  	                                                                *
    '*          : All Rights Reserved                                                                   *
    '*  Date    : 07/02/21                                                                              *
    '*  Device  : 16F1788                                                                               *
    '*  Version : 1    (PBP 3.0.10.4)                                                                   *
    '*  Notes   : Spurious "I'm Alive" serial traffic following RE action - what am I missing?          *
    '*          : REa portb.6, REb portb.5, REsw portb.4 all pulled up, active low.                     *
    '*          : 											                                            *
    '****************************************************************************************************
    '
    
    '=========================================================================================================
    '        CONFIGURE DEVICE 
    '=========================================================================================================
    #CONFIG ; 16F1788.
            __config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON &  _MCLRE_ON & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF
            __config _CONFIG2, _WRT_OFF & _VCAPEN_OFF & _PLLEN_OFF & _LVP_OFF & _BORV_HI & _LPBOR_OFF
    #ENDCONFIG
    
    ' -----[ Initialization ]------------------------------------------------------------------------------------------------ 
        TRISA = %00000000       ' Set I/O.
        TRISB = %01111111       ' Set I/O.
        TRISC = %00000000       ' Set I/O.
    
    define  OSC  4
            OSCCON = %01101010   ' 4Mhz (F1788).
    'define  OSC  32
    '        OSCCON = %11110000   ; 32 MHz, 
    
        ANSELA = 0           ; all digital          
        ANSELB = 0           ; all digital 
    
    ' Define LCD connections       
        DEFINE  LCD_DREG        PORTA	' LCD Data Port
        DEFINE  LCD_DBIT        0		' Starting Data Bit
        DEFINE  LCD_RSREG       PORTA	' Register Select Port
        DEFINE  LCD_RSBIT       4		' Register Select Bit
        DEFINE  LCD_EREG        PORTA	' Enable Port
        DEFINE  LCD_EBIT        7		' Enable Bit
        DEFINE  LCD_BITS		4		' Data Bus Size
        DEFINE  LCD_LINES		2		' Number of Lines on LCD
    
    '=========================================================================================================
    '        PIN ASSIGNMENTS, SYSTEM CONSTANTS, TEMPORARY VARIABLES
    '=========================================================================================================
    ' Rotary Encoder stuff
    Old_Bits    VAR BYTE bank0  ' Encoder port bits before change.
    New_Bits    VAR BYTE bank0  ' Encoder port bits after change.
    RotEnc1_val VAR word bank0  ' Connected to PORTB<5:6>.
    MaxLimit    var word bank0  ' Used for setting upper limit for encoder counts.
    RotEncDir   VAR BIT  bank0  ' Direction of rotation bit.
    ButtPush    var bit  bank0  ' Flag shows button was pushed, allows quick exit from Int handler.
    
                        
        Include "MODEDEFS.BAS"      ' Include Shiftin/out modes.
        INCLUDE "DT_INTS-14.bas"    ' Needs to be assembled with MPASM **********
        INCLUDE "ReEnterPBP.bas"    ; Include if using PBP interrupts	
    
        OPTION_REG.6 = 0        ' INTEDG set to interrupt on falling edge.
    '    OPTION_REG.6 = 1        ' INTEDG set to interrupt on rising edge.
    
    '   IOCBP = %01110000        ; REa portb.6, REb portb.5  pos edge trigger
       IOCBN = %01110000        ; REa portb.6, REb portb.5, REsw portb.4  neg edge trigger - falling edge only.
       IOCBF = 0                ; Reset the PortB flag.
       INTCON = %11001000       ; GIE, PEIE, IOCIE.
       
    '    RotEnc1_val = 0         '
        pause 500               ' Let everything settle...
    
    'latc.6 = 0                  ' Prevent garbled serial - newer PICs, true.        
    latc.6 = 1                  ' Prevent garbled serial - newer PICs, inverted.        
    serout2 PORTC.6,16468,[13,10,"I'm Alive",13,10,10]
    
    ASM
    INT_LIST  macro    ; IntSource,         Label,  Type, ResetFlag?
            INT_Handler    IOC_INT,  _Rot_Encoder,   PBP,  yes  ; Newer PICs.
        endm
        INT_CREATE              ; Creates the interrupt processor .
    ENDASM
    
    @    INT_ENABLE   IOC_INT   ; Newer PICs.
    Old_Bits = PORTB & (%01100000)  ; RE uses B.6,B.5 - REa and REb.
    'Old_Bits = PORTB & (%01110000)  ; RE uses B.6,B.5,B.4 - includes RE push button.
    
    
    Main:   
    goto main                       ' endless loop waiting for INT. 
    
    
       
    '---[IOC - interrupt handler]---------------------------------------------------
    Rot_Encoder:
        if PortB.4 = 0 then             ' Check if Encoder pushbutton was pushed.
    serout2 PORTC.6,16468,[13,10,"ButtPush = 1, RE ",dec5 RotEnc1_val]
        ButtPush = 1                    ' Set the flag for a quick exit.
    goto DoneRotEnc                     ' Clean up and exit.
        endif
    
    ' Don't forget 4 counts per detent (Quadrature).
         New_Bits = PORTB & (%01110000)         ' Set port bits for Encoder's chosen A and B channels.
         IF (New_Bits & %01110000) = (Old_Bits & %01110000) then DoneRotEnc ' No change.
         RotEncDir = New_Bits.6 ^ Old_Bits.5    ' Bit XOR compare to get direction of rotation.
         if RotEncDir = 0 then                  ' CCW so decrement.
            if PORTB.6 and PORTB.5 = 1 then     ' Only when RE both pins high (convert quad counts to singles).
            RotEnc1_val = RotEnc1_val - 1       ' Decrement the count.
              if RotEnc1_val => 65530 then RotEnc1_val = 0  ' Limit the max.
              if RotEnc1_val <= 4 then RotEnc1_val = 0      ' Limit the min.
    serout2 PORTC.6,16468,[13,10,"RE_val ",dec5 RotEnc1_val]
            endif
         ELSE           ' RotEncDir = 1, CW so increment.
            if PORTB.6 and PORTB.5 = 1 then     ' Only when RE both pins high (convert quad counts to singles).
              RotEnc1_val = RotEnc1_val + 1     ' Increment the count.
              if RotEnc1_val >= MaxLimit then RotEnc1_val = MaxLimit ' Limit the count.
    serout2 PORTC.6,16468,[13,10,"RE_val ",dec5 RotEnc1_val]
            endif
         ENDIF
    
    
    DoneRotEnc:
         Old_Bits = New_Bits
        INTCON.0 = 0        ; Clean up and exit - clear IOCIF.
        IOCBF = 0           ; Reset the flag.
    @ INT_RETURN
    
    end

Similar Threads

  1. Rotary encoder subroutine
    By Fanias in forum mel PIC BASIC Pro
    Replies: 1
    Last Post: - 10th October 2018, 14:13
  2. Using an incremental rotary encoder?
    By keithv in forum mel PIC BASIC Pro
    Replies: 5
    Last Post: - 12th January 2016, 23:23
  3. Instant Interrupts for rotary encoder
    By fnovau in forum mel PIC BASIC Pro
    Replies: 7
    Last Post: - 15th August 2014, 09:24
  4. New approach to Rotary Encoder
    By Ioannis in forum Code Examples
    Replies: 90
    Last Post: - 11th July 2013, 22:05

Members who have read this thread : 3

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