Rotary Encoder Tester

geezer88

Senior Member
I've been working on a project that would use a rotary encoder for user input. My first attempt was a miserable failure, and after a bit of head scratching I found my "new" surplus encoder was no good. So, to sort the good from the bad, I cobbled up a tester with my Picaxe test bed. First the hardware.

I am only using three I/O. B.1 and B.2 are the two encoder connections. B.5 is used to drive a serial display. In this case, the display is this one: http://www.picaxeforum.co.uk/showthread.php?20521-Inexpensive-Serial-(surplus)-LCD&highlight=surplus+dual+lcd

The encoder common pin is connected to ground, and a 10K resistor goes from +5 to each phase connection. The phase connections are then routed to the Picaxe inputs. Very simple; when an encoder switch closes the input is low and when it is open, the pullup resistor sends it high.

The program uses three lines of the display. Lines one and two show phases A and B graphically, with an underscore representing low, and a solid block for high. This makes it real easy to see the phase relationship change as the knob is turned left and right. The third line prints CW or CCW and a counter that starts at 100 to show how many "clicks" the knob has turned.

This will never be a finished product for me. The breadboard version works well enough, and as soon as I get a few good encoders squirreled away, it will become some other project.

Here's the code. I have made no attempt to optimize the code for speed or compactness, preferring a straight forward program that is easy to understand and use.

The direction of rotation uses the XOR of old phase A with new phase B. The best explanation I have seen is this one: http://www.tufts.edu/programs/mma/emid/RotaryEncoder.pdf

Because of the display activity and slow clock speed, a fast twist of the knob will get ahead of the Picaxe, so don't consider this code for any high speed need.

View attachment EncoderOutputAnalyser.bas


Hope this is of some use to the rotarians out there.

tom
 

Morganl

Senior Member
In a PIC assembly solution for an encoder i had a byte in which i shifted in the two new phase bits from LSB side, keeping the last two so total 4 bits.
Used the four bits for a to point to 16 item list of pointers to total 4 target routines:
Forward one step
Back one step
Both changed (sensor error / too slow processor / overspeed / glitch)
No chhange (OK if the routine is called asyncronously - error if routine is called by pin change interrupt)

In assembly it is veru easy and fast:
There is a assembly instruction to add to program counter, like on-gosub but many thousands time faster.
A PIC16@20 Mhz did 17k counts per sec incl postprocessing, math, ramps, SPI, and many other parallel tasks. (machine control)

Maybe a similar routine in PICAXE basic is faster then the proposed ones.

Not that this routine counts on all changes on both phases - highest possible resolution.
 

techElder

Well-known member
Morganl, perhaps you could post a flow chart of your routine, so someone could create the PICAXE code from that. Sounds interesting.
 

geezer88

Senior Member
Oh yeah, another small detail I learned. The common pin on some encoders is not the one in the middle.

It is pretty neat that even with another language, the structure of basic makes it pretty easy to follow.

tom
 
Oh yeah, another small detail I learned. The common pin on some encoders is not the one in the middle.

It is pretty neat that even with another language, the structure of basic makes it pretty easy to follow.

tom
Thx for your nice comment.
It's true , not all pinout is the same.
whole important , too - not all encoders works similar.

For my example code (link) , the encoder works as follows :

e.g. you turn left :
latched , both pins (A+B) are low.
so, first Pin A goes high and then Pin B.
then goes latch and both are low again.

e.g. you turn right:
the same, both Pins a low.
first Pin B goes high and then Pin A.
and when is latched, both again low.

even the code is german , i think it's quite easy to understand ,..... I hope so :confused: :eek:
 

geezer88

Senior Member
That is how my current encoder works as well. In the detent position, both phases are "made", and it goes through the complete four step sequence getting to the next detent. Therefore, each detent click produces four counts. This also produces a side effect that's not so great. As the knob is turned past center, it will accelerate down the ramp if I'm not holding it tight, and that extra speed will outrun the Picaxe. As Morganl responded, an assembly language routine would have no trouble with keeping up. In the past I've programed Atmel parts in compiled C, and they were extremely fast. I really appreciate the simplicity of Picaxe Basic for most things.

I guess your program is writting in "G-Basic". Mine is written in OF-Basic. OF stands for old fart. Anyway, it is fun to see how different folks code similar problems. Hippy, on this forum, is the one that can distill a program down to absolute minimum using all sorts of cleaver tricks. My old brain doesn't seem to work that way, but I sure like to read his posts.

tom
 
I guess your program is writting in "G-Basic".
My Code is simply Picaxe-Basic :)
a picaxe 08M2 don't accepts other languages.

You can get encoder with several increments : 15 ...30 ...60 ...
If you use encoder with lower increments , e.g. 15 Steps , it's not impossibly to evaluate 20 Steps/sec. and more.
I think , it's depend , how will deployed the encoder.
 

PieM

Senior Member
This routine counts on all changes on both phases ...

Code:
'================================
'décodeur quadrature Version 02
'PieM 140505
'================================

#picaxe 08M2

symbol InputA 		= pinc.3
symbol InputB 		= pinc.4

symbol Comb_act  	= b0	'combinaison actuelle
symbol Comb_Preced 	= b1	'combinaison pecédente
symbol Compt		= w3	'compteur
'________________________________________________
main:

do
 	bit0 = InputA
 	bit1 = InputB
 	
	if Comb_act <> Comb_Preced then 
		Comb_Preced = Comb_Preced * 2  xor Comb_act
		if bit9 = 0 then
			inc Compt
		else 
			dec Compt
		endif
		Comb_Preced=Comb_act
	endif
loop
 
Last edited:

geezer88

Senior Member
This routine counts on all changes on both phases ...

Code:
'================================
'décodeur quadrature Version 02
'PieM 140505
'================================

#picaxe 08M2

symbol InputA 		= pinc.3
symbol InputB 		= pinc.4

symbol Comb_act  	= b0	'combinaison actuelle
symbol Comb_Preced 	= b1	'combinaison pecédente
symbol Compt		= w3	'compteur
'________________________________________________
main:

do
 	bit0 = InputA
 	bit1 = InputB
 	
	if Comb_act <> Comb_Preced then 
		Comb_Preced = Comb_Preced * 2  xor Comb_act
		if bit9 = 0 then
			inc Compt
		else 
			dec Compt
		endif
		Comb_Preced=Comb_act
	endif
loop
Your routine is very compact and effective. It is a more elegant version of what I was doing. I'll keep it for future use.

tom
 

PieM

Senior Member
The last one ... more compact !

Code:
'================================
'décodeur quadrature Version 03
'PieM 140505
'================================

#picaxe 08M2

symbol InputA 		= pinc.3
symbol InputB 		= pinc.4

symbol Comb_act  	= b0	'combinaison actuelle
symbol Comb_Preced 	= b1	'combinaison pecédente
symbol Compt		= w3	'compteur
'________________________________________________
main:

do
 	bit0 = InputA
 	bit1 = InputB
 	
	if Comb_act <> Comb_Preced then
	     Compt = bit1 xor bit8 *2 + compt - 1
	     Comb_Preced=Comb_act
	endif
loop
 

Morganl

Senior Member
Nice.

Below find my take on my jump table method ported to picaxe, untested.
It is a piece of larger code for a panel unit with display, knob, buttons.
It is intended to be goto:ed frequently.
(ugly exits with goto, because return is slow on picaxe...)

Code:
TH_Biph: ; biphase / quadrature / 2-bit gray...  decoding ; for incremental "potentiometer" or position sensor
; Special needs: reserve variable b1 for this task! (utilising speedup from directly acessing bits in the byte)

;Method: assemble the phases status this pass and the former pass in a variable, and let it control where we jump...
;It counts on every transition of the two signals. Therefor it double steps on some panel "knobs" designed for simpler logic.
;it is very fast as it contain no conditional execution and only one internal jump (on..goto)

b1=b1/4 ; right shift 2 step; last checked phases are now in bits 1,0 of b1.
bit10=c.0 ;set bits 2 and 3 in b1 to same status as the input phases from the knob/incremental sensor...
bit11=c.1 ;.. so we now have a 4-bit value that we use directly to know what happened:
on b1 goto bis,biu,bid,bie,	bid,bis,bie,biu,	biu,bie,bis,bid,	bie,bid,biu,bis
; (for readability i keep the labels short)
bie: ;Error: both phases changed.
	; For panel use do not care, but if for machine positon then emergency stop it! 
bis: ;Same position (or possibly we went a whole cycle, but probably we would get Error earlier) ; no phase changed, just exit
goto Engine_NextHT
biu: ;Up
inc BiphCount
goto Engine_NextHT
bid: ;Down
dec BiphCount
goto Engine_NextHT
 

hippy

Technical Support
Staff member
Code:
b1=b1/4 ; right shift 2 step; last checked phases are now in bits 1,0 of b1.
bit10=c.0 ;set bits 2 and 3 in b1 to same status as the input phases from the knob/incremental sensor...
bit11=c.1 ;.. so we now have a 4-bit value that we use directly to know what happened:
on b1 goto bis,biu,bid,bie,	bid,bis,bie,biu,	biu,bie,bis,bid,	bie,bid,biu,bis
I am guessing those bit assignments for pin inputs should actually be -

bit10 = pinc.0
bit11 = pinc.1
 

geezer88

Senior Member
Morganl, I used your routine in my tester and it definitely is more zippy. Thanks for posting that. I added an error counter, and it is still pretty easy to over speed the knob and get errors. I wish I had some more encoders, because I see more errors in one direction than the other, and I would bet that is an encoder build or design issue.

I also played around with the surplus display, and got it to accept 9600 baud communications. That required increasing the picaxe frequency to be raised to 8mHz, so all these changes muddy up the improvement waters. I plan to do some logic analyzer testing to sort out some of the timing on a more objective manner. I'll post what I learn.

Here's the Morganl routine version:

Code:
#rem  **rotary encoder output analyser 5-6-14
********encoder pins on b.1 and b.2
********serial out on b.5
********dumped the logic diagram
********using the routine from Morganl on Picaxe forum
#endrem

#picaxe 18m2
#no_data

setfreq m8

symbol clik = b0						'charactor counter
symbol state = b1   					'state of all old and new bits contained herein
symbol tens = b2
symbol ones = b5
symbol huns = b8					'shared with posn
symbol fubar = b12   					'error count

pause 500   						'time for LCD to initialize

bit11 = pinb.1  						'get current state of encoder pins
bit10 = pinb.2

clik = 100  							'initialize clicks to a positive number since no negatives allowed
fubar = 0

serout b.5, n9600_8, (21, 192)
serout b.5, n9600_8, (22, "Cnt=")
bintoascii clik, huns, tens, ones
serout b.5, n9600_8, (22, huns, tens, ones)
goto PrintError

MainLoop:

b1=b1/4 		'right shift 2 step' last checked phases are now in bits 1,0 of b1.
bit10=pinb.2	'set bits 2 and 3 in b1 to same status as the input phases from the sensor
bit11=pinb.1	'now have a 4-bit value that we use directly to know what happened:

on b1 goto bis,biu,bid,bie, bid,bis,bie,biu, biu,bie,bis,bid, bie,bid,biu,bis

bie:			'Error: both phases changed. Panel use; do not care, Machine position; then emergency stop it!
inc fubar
goto PrintError

bis:			'Same position no phase changed, just exit
goto MainLoop

biu:			'Up
	inc Clik
	serout b.5, n9600_8, (21, 196)
	bintoascii clik, huns, tens, ones
	serout b.5, n9600_8, (22, huns, tens, ones)
	goto MainLoop

bid:			'Down
	dec Clik
	serout b.5, n9600_8, (21, 196)
	bintoascii clik, huns, tens, ones
	serout b.5, n9600_8, (22, huns, tens, ones)
	goto MainLoop
end

PrintError:
serout b.5, n9600_8, (21, 201)
serout b.5, n9600_8, (22, "Err=")
bintoascii fubar, huns, tens, ones
serout b.5, n9600_8, (22, huns, tens, ones)
goto MainLoop

tom
 

Morganl

Senior Member
geezer88, nice to see.

I believe the serout lines take much more time than the decoder...
Try to limit to just one character to send for each run of the encoder routine.

Also jumps could be eliminated by replacing "bis" in the on-gosub with "MainLoop",
(less elegant but faster)
and move the PrintError: routine so it replaces "goto PrintError"
 

erco

Senior Member
Ebay China has pretty nice 400 and 600 PPR quadrature encoders in the $15 price range, I bought one. Big, beefy enclosed units with ball bearings should work well in a moderate-duty application, such as a freewheeling caster on a dead reckoning robot.

600 PPR $15.25 http://www.ebay.com/itm/600P-R-Photoelectric-Rotary-Encoder-Incremental-5-24V-AB-Two-Phases-Shaft-6mm-/251481973342

400 PPR $13.48 http://www.ebay.com/itm/400P-R-incremental-rotary-encoder-400-pulses-MAX-mechanical-speed-of-1000/330952664057
 
Top