PIC – Reading rotary encoder switches

PIC – Reading rotary encoder switches

PIC - Reading rotary encoder switches - Jameco 776386

Mechanical Rotary Encoder Switch (Jameco)

These are inexpensive, small, mechanical front-panel-type rotary switches that output a 2-bit grey code.  I used a part # 776386 (datasheet) from Jameco.   This version has 24 detents.

The Grey Code output ensures that only a single bit changes at any given time.  Of course, as this is a mechanical switch, you should debounce the inputs first.  Look here for A Guide to Debouncing by Jack G. Ganssle.

How the switch ‘behaves’

PIC - Reading rotary encoder switches - Jameco 776386 Output Table

776386 Output Table

The switch has 24 detent positions for a full 360° rotation.  Each movement from detent-to-detent goes through the complete cycle shown here – a total of 4 state changes for each detent (or 48 state changes per revolution).

The routine below looks for 4 state changes before making a one-bit change to the output counter SwitchValue. Think of Up/DownPrescale as a 2 bit (divide-by-4) prescaler in front of SwitchValue.

 

I have a generic debounce routine (add link when I get around to it)

The routine below is called when a change in the switch status is detected (done by the calling routine) and requires only one input: the debounced switch state (Port_Old).

Exposed “methods and properties”

  • InitReadEncoder: Called once during processor initialization routine.
  • ReadEncoder: called every time a change in the rotary encoder inputs are detected (and debounced)
  • SwitchValue: The ‘count’ of the number of steps/turns

Program Flowchart

PIC - Reading rotary encoder switches - Rotary Encoder Flowchart

Rotary Encoder Program Flowchart

Flowchart (Rotary Encoder Rev02).  You will find the flowchart and the code listing are very well aligned for easy understanding.

The code uses an 8 bit variable (SwitchValue) to keep track of the ‘volume’ (for lack of a better word).

Program Code

;*******************************************************************************
;*    Filename: Read Encoder.asm
;*    Date: 27 April 2015
;*    File Version:  02
;*    Author:    Brian Volken
;*    Company:     Brady Volken Enterprises
;*    Description: Basic routine to read single mechanical rotary encoder
;*******************************************************************************
;*    Revision History:
;*    02 Replaced single prescaler (SwitchCount) with two prescalers
;*       UpPrescale and DownPrescale.
;*    01 Initial Version
;*******************************************************************************

;*******************************************************************************
;* Calling Instructions for this module
;*******************************************************************************
;
;*
;* This is for any details related to calling this module
;*
;*
;*
;*
;*
;******************************************************************************

;*******************************************************************************
; Processor Inclusion
;*******************************************************************************

   #include p18f4580.inc

;*******************************************************************************
; Define Variable Definitions
;*******************************************************************************

    UDATA_ACS
SavedSwitchPos	RES	1	    ; holds last, rotated switch position
SwitchValue		RES	1	       ; this holds the number of counts of rotation 256 values
UpPrescale  	RES	1      ; it takes 4 transitions, detent-to-detent as a single "step"
DownPrescale  	RES	1	    ; it takes 4 transitions, detent-to-detent as a single "step"

;*******************************************************************************
;* Defines
;*******************************************************************************
#define  TicksPerStep  	0x04	; it takes 4 transitions, detent-to-detent as a single step

; TODO PLACE ASSEMBLER VARIABLE DEFINITIONS HERE
; cCLOCK  set  8000000        ;Clock Speed

;*******************************************************************************
;
; "Exposed" sub routine elements (variables & code)
; GLOBAL is used to make a lable visible to other files.
; EXTERN must be used in the file that uses (calls) the lable to make it visible
;
;*******************************************************************************

; These routines are exposed by Read Encoder.asm.asm
    global SwitchValue
    global InitReadEncoder, ReadEncoder

    extern  Port_Old, Port_Changed

; To be copied into Calling Routine
;    ; These routines are in Read Encoder.asm
;    extern SwitchValue
;    extern InitReadEncoder, ReadEncoder

;*******************************************************************************
;* Macros
;*******************************************************************************

;* Enable & Disable Macros
mEnableGI   macro
    BANKSEL INTCON
    BSF     INTCON, GIE
    endm
mDisableGI  macro
    BANKSEL INTCON
    BCF      INTCON, GIE
    endm

;*******************************************************************************
;* Initilization Code Section
;*******************************************************************************
;
;*
;* This is any necessary notes for how the init settings are derived
;*
;*
;*
;*
;*
;*
;*
;*
;*
;******************************************************************************

Read_Encoder CODE

InitReadEncoder	;Called at boot time
; Init Variables
    	CLRF	SavedSwitchPos
    	CLRF	SwitchValue
    	MOVLW	TicksPerStep
    	MOVWF	DownPrescale
    	MOVWF	UpPrescale
    RETURN


;*******************************************************************************
;* Read Encoder
;*******************************************************************************
ReadEncoder
; Disable Global Interrupts
;   mDisableGI
; Read current switch pos, mask unused bits
    	MOVLW   B'00000001'
    	ANDWF	Port_Old, w ; Port_Old = debounced switch state

; XOR with SavedSwitchPos
    	XORWF	SavedSwitchPos, w
    	BTFSC	STATUS, Z
    GOTO SwitchDown
    	GOTO SwitchUp

;*******************************************************************************
;* Switch Up
;*******************************************************************************
SwitchUp
; Have we made 4 phase changes?
; Decrement UpPrescale
    DECFSZ	UpPrescale, f
    GOTO    RotateSave

; Reached zero, reinit UpPrescale
    MOVLW	TicksPerStep
	    MOVWF	UpPrescale                      ; not 4 steps yet

; SwitchValue at max 'volume'?
    	MOVLW	0xFF
    	XORWF	SwitchValue, w
    	BTFSC	STATUS, Z
    GOTO    RotateSave                    ; we can't go higher than max

; Increment SwitchValue
    INCF    SwitchValue, f

    GOTO    RotateSave

;*******************************************************************************
;* Switch Down
;*******************************************************************************
SwitchDown
; decrement SwitchCount
    	DECFSZ	DownPrescale, f
    GOTO    RotateSave                  ; Not yet made 4 phase changes

; Reached zero, reinit DownPrescale
    MOVLW	TicksPerStep
    	MOVWF	DownPrescale

; SwitchValue to zero?  (e.g. at it's lowest setting)
    	MOVF	SwitchValue, f
    	BTFSC	STATUS, Z
    GOTO    RotateSave                  ; can't go below zero

; Decrement Switch Value
    DECF    SwitchValue, f

;   GOTO    RotateSave

;*******************************************************************************
;* Rotate & Save
;*******************************************************************************
RotateSave
; read current switch position and mask unused bits
    	MOVFF	Port_Old, SavedSwitchPos

; rotate one bit to right
    RRNCF	SavedSwitchPos, f

; mask unused bits
    	MOVLW	B'00000001'
    	ANDWF	SavedSwitchPos, f

;    GOTO    ExitReadEncoder


;*******************************************************************************
;* Exit Read Encoder
;*******************************************************************************
ExitReadEncoder

    MOVLW	B'11111100'
    ANDWF	Port_Changed, f

; Re-enable Global Interrupts
;   mEnableGI
    RETURN
	END