There exist a number of ready-to-go chips that can convert the output of incremental (quadrature) encoders into counts, count and direction or count-up and count-down signals. This projects uses an 8-pin ATtiny processor to flexibly perform this conversion for a fraction of the cost of commercial devices.

Several applications make use of incremental shaft encoders. Many of the 'soft' front panel controls on electronic equipment use them. The4se are the kind that spin without stops - a spin clockwise increases a quantity, a spin anticlockwise decreases it. Motor speed/position sensors also produce quadrature signals as the shaft is rotated. A mechanical mouse has a slotted wheel and a pair of sensors that generate quadrature signals when the mouse is moved. The chip inside the mouse is a simple processor which can detect this quadrature signal and keep count of distance and direction, communicating the results periodically with the main computer.

A typical, ideal two-channel quadrature signal looks like this:

View this as a time-dependant graph as if on an oscilloscope. The sequence begins at the left.

1X decoding
The simplest way to use the output would be to take channel A as the input to a counter. However, that would tell us nothing about the direction of the count. Suppose however that the counter increments on the rising edge of A. You will be able to see that if we are scanning from left to right, immediately after a rising edge on channel A, channels A and B have different values. Equally, if we reverse the direction and scan from right to left, after a rising edge on channel A, both channels have the same value. Thus, we can test for equality of channels A and B to determine the direction while channel A generates the count. This scheme works very well. even if your encoder dithers back and forward around a channel A edge, the count simply increments and decrements alternately.

2X decoding
A shortcoming of the previous method is that the count frequency is the same as the frequency of channel A. Thus, an encoder said to have a resolution of 500 counts per revolution (cpr) does exactly that. We can do better by using both edges of Channel A . This is not too hard to arrange in hardware but this uses up valuable board space. The equality test described just now works just as well if we are detecting falling edges. Thus we can use the same routine for both rising and falling edges and detect twice as many transitions. With 2X decoding our 500 cpr encoder can generate 1000 counts per revolution.

4x decoding
It is possible to do even better if we examine the edges of both channel A and channel B. There are four edges for each phase of channel A and it is possible to get 2000 counts per revolution from our 500 cpr encoder.

If you have a processor with the ability to generate an interrupt on pin change it is pretty simple to get the count we want. I had a number of Atmel AVR ATtiny13 processors available which are 8 pin devices with an internal 9.6MHz clock. Channel A is connected to the INT0 pin and channel B is connected to the INT1 pin. Only the INT0 interrupt is used here as I needed only 1X or 2X decoding. The ATtiny can generate an interrupt on either a rising or a trailing edge. For 2X decoding, the sense of the interrupt is changed after each interrupt so that the routine responds alternately to rising and falling edges. On each interrupt, the direction of the count is determined and a pulse generated on either the count-up or the count-down pin as appropriate. On the processor used, pulse rates as high as 150kHz should be readily detectable. The interrupt routine takes less than 4.5us to execute and rates as high as 220kHz may be possible with inputs that are in perfect quadrature. That is unlikely in practice.

Source Listing:

;===========================================================
; QUD
;
; P Harrison 29/11/05
; peter.harrison@helicron.net
; If you use this code then at least let me know.
; Quadrature decoder with choice of 1X or 2X decode
;
; connect A and B phase of the quadrature signal
; to PORTB.0 and PORTB.1 (pins 5 and 5 on the ATTiny13
;
; count up and count down pulses will appear on
; PORTB.2 and PORTB.3 (pins 3 and 7) as the count progresses
;
; PORTB.4 (pin 4) can be left floating for 2X decode
; or tied to ground for a 1X decode
;
; The pulses are about 2us long
;===========================================================

.LISTMAC

.EQU PINB=0x16
.EQU DDRB=0x17
.EQU PORTB=0x18

.EQU WDTCR=0x21
.EQU CLKPR=0x26
.EQU MCUSR=0x34
.EQU MCUCR=0x35

.EQU GIFR=0x3A
.EQU GIMSK=0x3B
.EQU SPL=0x3D
.EQU SREG=0x3F

.CSEG
.ORG 0

;INTERRUPT VECTORS
RJMP __RESET
RJMP _INT0_ISR
RJMP 0x00
RJMP 0x00
RJMP 0x00
RJMP 0x00
RJMP 0x00
RJMP 0x00
RJMP 0x00
RJMP 0x00

__RESET:
LDI  R30,LOW(0x9F)  ; stack pointer to top of ram
OUT  SPL,R30
_main:
LDI  R30,0x80       ; system clock is crystal/1
OUT  CLKPR,R30
LDI  R30,0
OUT  CLKPR,R30
LDI  R30,0x0C       ; set PORTB.2 and PORTB.3 as outputs
OUT  DDRB,R30
LDI  R30,0x3F       ; activate the pull ups on the inputs
OUT  PORTB,R30
LDI  R30,0x40
OUT  GIMSK,R30
LDI  R30,0x02
OUT  MCUCR,R30
LDI  R30,0x40
OUT  GIFR,R30
SEI
_loop:
RJMP _loop

_INT0_ISR:
LDI  R30,0            ; test PINB.0 and PINB.1 for equality
SBIC PINB,1
LDI  R30,1
LDI  R31,0
SBIC PINB,0
LDI  R31,1
CP   R30,R31
BRNE _DOWN_COUNT
SBI  PORTB,2          ; bits equal => count up
RJMP _DECODE_TYPE
_DOWN_COUNT:
SBI  PORTB,3          ; bits different => count down

_DECODE_TYPE:
SBIS PINB,4           ; what kind of decoding do we want?
RJMP _MAKE_PULSE      ; pull PINB.4 low for 1X decoding
IN   R30,MCUCR        ; if we want 2X decoding then we
LDI  R26,0x01         ; toggle the sense of the interrupt edge
EOR  R30,R26          ; at each interrupt to detect both edges
OUT  MCUCR,R30

_MAKE_PULSE:
LDI  R24,5            ; short delay to get a proper pulse
_DELAY:
DEC  R24
BRNE _DELAY

CBI PORTB,3           ; clear the bits again
CBI PORTB,2
RETI