PIC – Reading rotary encoder switches
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’
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
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